Original text:
If a promised server push is not needed by the client, the client SHOULD send a CANCEL_PUSH frame.  If the push stream is already open or opens after sending the CANCEL_PUSH frame, a QUIC STOP_SENDING <del> frame with an appropriate error code can also be used (e.g., HTTP_PUSH_REFUSED, HTTP_PUSH_ALREADY_IN_CACHE; see errors).  This </del>  asks the server not to transfer additional data and indicates that it will be discarded upon receipt. A client-only setting was sent by a server, or a server-only setting by a client. <del> The server has attempted to push content which the client will not accept on this connection. </del>  An internal error has occurred in the HTTP stack. <del> The server has attempted to push content which the client has cached. </del>  <del> The request or its response is cancelled. </del>  The client's stream terminated without containing a fully-formed request.

Reviewer comments:
My intention was to keep PRs somewhat atomic and fragment the error code space in each PR that separately removes or consolidates an error code. Once complete, I would then defrag it on a PR that let's people weigh in on which error code should have which number. (It also simplifies dependencies which having branches off branches) Happy to do something different if you want.
RFC 7540 section 8.2.2 states: receive the pushed response from the server or if the server takes too long to begin sending the promised response, the client can send a RSTSTREAM frame, using either the CANCEL or REFUSEDSTREAM code and referencing the pushed stream's identifier. HTTP/3 changed push a bit and introduced two error codes: HTTPPUSHREFUSED and HTTPPUSHALREADYINCACHE. These is extra fidelity on top of HTTP/2, clearly articulating if the pushed item is in cache or not. This may be seen as useful, however there are some practical downsides: The CANCEL_PUSH frame exists and is the first RECOMMENDED action for declining a push. This does not expose the reason In actuality, server push caching is murky, which may affect the amount of confidence an operator could put into analyising these error codes. I think it is simpler, more self-consistent, and more HTTP/2-consistent to have one error code for server push. I'll prepare a PR for this.
I agree that we can remove HTTPPUSHALREADYINCACHE. In addition, I think we might also want to remove CANCELPUSH in favor of using REQUESTCANCELLED. IIUC, the semantics of CANCELPUSH can be (or can be) exactly the same as REQUESTCANCELLED after the client has sent the entire request.
Clarification, do you mean: replace the HTTPPUSHREFUSED error with HTTPREQUESTCANCELLED error? remove CANCELPUSH frame altogether and replace the function by sending a STOPSENDING with HTTPREQUESTCANCELLED error? I don't think option 2 is possible, the purpose of CANCEL_PUSH frame is to stop pushes before any stream is created - at that point there is no stream ID to reference.
Also, option 1 then deviates from HTTP/2 that had "REFUSED_STREAM" and "CANCEL" error codes/
Apologies. I meant option 1. REFUSEDSTREAM (of HTTP/2) maps to HTTPREQUESTREJECTED (of HTTP/3). CANCEL (of HTTP/2) maps to HTTPREQUESTCANCELLED (of HTTP/3). Therefore, it is safe to combine HTTPPUSHREFUSED to HTTPREQUESTCANCELLED. HTTPPUSHREFUSED error is used after the server processes the request, therefore the semantics is same as CANCELL, not REFUSEDSTREAM.
Thanks for clarifying, I agree with this and will incorporate it into this PR.
Define terms for the operations which an application can cause a transport to take, and for the events of which the application can be notified by the transport. This should in the process. This issue should: Eliminate the term "trigger" when talking about QUIC frames Try not to mention specific QUIC frames at all Remove "send a QUIC RESETSTREAM" from 7.2.4 Remove "sends a STOPSENDING with HTTPREQUESTCANCELLED, a server MAY send the error code HTTPREQUESTREJECTED in the corresponding RESET_STREAM" from 4.1.2 Reword references to sending values in particular transport parameters I believe those essential functions are: On the connection: Open a connection, specifying certain properties that translate to transport parameters Receive incoming connections, which will possess certain properties based on the peer's transport parameters Participate in deciding whether to accept 0-RTT based on embedded data Provide data to be embedded in resumption tokens Cooperate to control keep-alive, either by sending PINGs directly or by telling the transport that the connection can't be allowed to time out Immediate close On the stream: Read data Write data, understanding at what point flow control credit has been committed Close the sending part Request closure of the receiving part The events we depend on are connection closure and stream closure. This is largely editorial, since it's mostly defining terminology, but we'll also likely be mandating that QUIC stacks expose these features, so that's normative/design. (Obviously the 0-RTT ones will be "if implemented" requirements.)

Revised text:
If a promised server push is not needed by the client, the client SHOULD send a CANCEL_PUSH frame.  If the push stream is already open or opens after sending the CANCEL_PUSH frame, a QUIC STOP_SENDING  <ins> frame with an error code of HTTP_REQUEST_CANCELLED can be used.  This </ins> asks the server not to transfer additional data and indicates that it will be discarded upon receipt. A client-only setting was sent by a server, or a server-only setting by a client.  <ins> This code is reserved and has no meaning. </ins> An internal error has occurred in the HTTP stack.  <ins> This code is reserved and has no meaning. </ins>  <ins> The request or its response (including pushed response) is cancelled. </ins> The client's stream terminated without containing a fully-formed request.