Change:
server will not have discarded the state.  Clients MUST NOT send the "SETTINGS_NUM_"S" setting; receipt of this setting by a server MUST be treated as a connection error of type <del> "HTTP_WRONG_SETTING_DIRECTION". </del> <ins> "HTTP_SETTINGS_ERROR". </ins> Client-controlled placeholders are identified by an ID between zero and one less than the number of placeholders the server has

Discussion:
This in a different way by subtituting the error code and not changing the requirements related to NUM_"S.
HTTPWRONGSETTINGDIRECTION presently applies to exactly one case, a client that sends NUM"S. Declassifying this as an error seems like a sensible option. This frees up error code 0x1, which we can move HTTPGENERALPROTOCOL_ERROR to (making HTTP/3 more similar to HTTP/2). My initial preference is to state that servers MUST ignore this value if present. I'll prepare a PR to that effect.
I forgot to mention that NAME made an observation that SETTINGS errors are presently also covered by MALFORMEDFRAME, which is also on the chopping block. An alternative solution is therefore to create an that covers all the ways that a SETTINGS frames could go wrong.
Thank you for opening the issues. While I agree that we do not need an error code specific to SETTINGS going in the wrong direction, I am not sure if declassifying it as an error is the right option. Not that I have a strong opinion, but IIRC our consensus is to be more strict against misbehaving peers in HTTP/3 compared to HTTP/2. MALFORMEDFRAME or INVALIDSETTINGS sounds like a more conservative fix to me.
Thanks NAME after suggesting this I changed my mind. I made to implement the change as you and Mike suggest. The nice side effect is that it takes one step away from MALFORMED_FRAME.
The HTTP draft doesn't have any SETTINGS parameters with a maximum value, but QPACK does. I don't see any text in hpack sec. 4.2.5.1 or qpack sec 5 that explains what to do if we receive a parameter that exceeds the maximum parameter.
Good observation. This is another case where MALFORMEDFRAME is not appropriate. I might reset the peer-initiated stream with HTTPLIMITEXCEEDED, which would prompt the other end to close the connection HTTPCLOSEDCRITICALSTREAM. Note that this changes how the error code is currently defined. An alternative is just CONNECTIONCLOSE with HTTPGENERALPROTOCOLERROR. ... or We could invoke the YAEC label :) Whatever we decide, I think it needs documenting.
Technically, you can't reset a peer-initiated stream, just STOP_SENDING.

That's an astute observation and something I've missed. Perhaps I'm missing it but the spec doesn't say how to handle a STOPSENDING received on a control stream. Should this peer issue a RESETSTREAM in response (causing the other peer to blow up) or should it just close the connection itself?
My understanding is that STOPSENDING is an act of closing a stream rather than an advisory signal. Based on that, I'd argue that receipt of STOPSENDING on a critical stream should trigger a HTTPCLOSEDCRITICAL_STREAM connection-level error.
I don’t think that STOP_SENDING terminates a stream per se, but an application can choose to interpret it as such, so HTTP/3 can make that call.
To be precise, STOPSENDING "typically indicates that the receiving application is no longer reading data it receives from the stream, but it is not a guarantee that incoming data will be ignored" (). Because there is a chance of it indicating that the peer is no longer reading, STOPSENDING received on a critical stream ought to trigger a HTTPCLOSEDCRITICAL_STREAM error.
I'd be happy with MUST close connection in response
Discussed in London; generalize to a "SETTINGS error" which covers multiple ways you can screw up the SETTINGS frame. This is one of them.
Resolved by and .
Looks good!