Original text:
Let this_key be the result of running Parsing a Key (parse-key) with input_string. <del> If dictionary already contains the name this_key, there is a duplicate; fail parsing. </del> If the first character of input_string is "=": Consume the first character of input_string.

Reviewer comments:
NAME thanks for the review, see latest commit. is talking about the abstract model for dictionaries, so they are required to be unique. I've removed "required to be" so that people don't misread this.
allows a sender to generate multiple header fields with the same name, if the field is defined as a comma-separated list [i.e., #(values)], and that such duplicates are semantically equivalent to them concatenated into a single header field. I think that this property has been useful for us, when annotating header fields. Can we allow such duplicates for Structured Headers too, when a list or a dictionary is being used? Note that we have merged based on the assumption that it is okay to require the consumer of SH header fields to have the knowledge on what the top type is.
That's always been the intent; are you saying that we should have text specifically enabling this for serialisation of dictionaries and lists?
Good to know that that has been the intent. Yes, I think we should clarify that explicitly for dictionaries and lists, because RFC 7230 can be interpreted to allow duplicates only for those using notation (which we do not use in SH). Also, it might be a good idea to revisit the rule for duplicate dictionary values, as I'd assume that people would try to append a header field without checking the values that already exist. IMO, the rule should be that the dictionary element that appears later overwrites the one that precedes.
PTAL
NAME Thank you! cb76bf1 looks good to me. Is your intention to retain the forbidding of duplicate dictionary values, even when the dictionary members are split into different header fields? For example, in the hypothetical world where cache-control is sent using SH, all the cache-control directives in the following example would be considered invalid and ignored. I am not strongly opposed to that, though my weak preference goes to requiring decoders to use the dictionary member that appears later when there is a collision.
NAME the problem is that ignoring duplicates can cause security issues for some headers (the headers that WebAppSec in the W3C are particularly sensitive about this). The only way to address it would be to add a flag to the API to control what to do with dups -- but that adds complexity (more to specifications than to implementations, but still). I'm not crazy about that; what do others think?
NAME Thank you for the explanation. But I wonder if it is actually the case that what those specifications forbid is having duplicate header fields, rather than forbidding duplicate dictionary members (or maybe both). By quickly searching on URL, I was able to find the prohibition on having duplicate header fields, but could not find a rule that forbids having duplicate policy tokens. Assuming that at least such specifications forbid having duplicate header fields, and based on the fact that SH draft does not forbid that (when a list or dictionary is used), I think there is no reason for the SH draft to forbid having duplicate dictionary members.
NAME NAME any comments re: the above? Specifically, if SH swallowed duplicate dictionary values rather than raising an error, would that be a concern from a security standpoint?
Given a header like , it seems like we have a few options: Fail parsing. Return . Return . Return . I think I can come up with arguments for any of these options, though I'd find 4 a bit surprising as a spec author, insofar as I'd have to deal with unexpected lists for all my dictionary values. of URL skips duplicate CSP directives. That is, it picks 2 out of the list above, choosing to retain only the first value for a given name. I do wonder whether that was the right decision, but it is consistent across browsers and provides developers with predictable behavior. We throw a warning into the console in Chrome for this case, and that's probably Good Enough.
This sounds like a bad API for working with structured header values. I'd hope that eventually we offer better APIs for operating on (what Fetch calls) header lists. I think historically there were some arguments for always using first or last based on likelihood of where header injection would happen. But I'm not sure how sound any of that rationale was. I don't think a client can reasonably protect against it. Therefore, what's most important is that the result is unambiguous and that it doesn't matter how you "spell" multiple values (commas or multiple fields or a combination) in the traditional header syntax.
Anyone have preferences between Mike's options 2 and 3?
NAME I think option 3 is preferable, because that would allow server administrators to override parameters by appending header fields. I agree that NAME that it's a hack, though sadly, I do not think we can get rid of such practice through API design on the server side. People are so used to dealing with each line of header fields than working through a API that deals with a set of header fields as a whole.
There are CDNs that offer JS or Wasm APIs effectively using et al. Once structured header values are more baked and have dedicated on-the-wire support I'd definitely like to add first-class support to objects.
I weakly prefer "first one wins" (2). It's only an aesthetic preference, however: I don't have any principled reason to pick 2 over 3 or vice-versa. I can live with either for the reasons NAME spells out.
If I had pick, I probably agree with NAME reasoning.
PTAL
Thank you for the changes. Looks good to me now. FWIW, I was bit surprised to learn that we do not mention how a duplicate is processed alongside the type definitions in . I checked and now I understand that the formal definition is provided in section 4.

Revised text:
Let this_key be the result of running Parsing a Key (parse-key) with input_string.  If the first character of input_string is "=": Consume the first character of input_string.