Original text:
server can promise.  A server SHOULD use Push IDs sequentially, starting at 0.  A client MUST treat receipt of a push stream with a Push ID that is greater than the maximum Push ID as a connection <del> error of type HTTP_LIMIT_EXCEEDED. </del>  The header of the request message is carried by a PUSH_PROMISE frame (see frame-push-promise) on the request stream which generated the Each Push ID MUST only be used once in a push stream header.  If a push stream header includes a Push ID that was used in another push stream header, the client MUST treat this as a connection error of <del> type HTTP_DUPLICATE_PUSH. </del>  6.2.3. A PRIORITY frame that references a non-existent Push ID, a Placeholder ID greater than the server's limit, or a Stream ID the client is not yet permitted to open MUST be treated as a connection <del> error of type HTTP_LIMIT_EXCEEDED. </del>  A PRIORITY frame received on any stream other than the control stream MUST be treated as a connection error of type HTTP_WRONG_STREAM. See QPACK for more details. A server MUST NOT use a Push ID that is larger than the client has <del> provided in a MAX_PUSH_ID frame (frame-max-push-id) and MUST NOT use the same Push ID in multiple PUSH_PROMISE frames.  A client MUST treat receipt of a PUSH_PROMISE that contains a larger Push ID than the client has advertised or a Push ID which has already been promised as a connection error of type HTTP_MALFORMED_FRAME. </del>  If a PUSH_PROMISE frame is received on the control stream, the client MUST respond with a connection error (errors) of type (see frame-push-promise).  A MAX_PUSH_ID frame cannot reduce the maximum Push ID; receipt of a MAX_PUSH_ID that contains a smaller value than previously received MUST be treated as a connection error <del> of type HTTP_MALFORMED_FRAME. </del>  7.2.9. Push ID that is larger than the client has provided in a MAX_PUSH_ID frame (frame-max-push-id).  A client MUST treat receipt of a DUPLICATE_PUSH that contains a larger Push ID than the client has <del> advertised as a connection error of type HTTP_MALFORMED_FRAME. </del>  This frame allows the server to use the same server push in response to multiple concurrent requests.  Referencing the same server push A frame was received on a stream where it is not permitted. <del> A Stream ID, Push ID, or Placeholder ID greater than the current maximum for that identifier was referenced. </del>  <del> A Push ID was referenced in two different stream headers. </del>  A unidirectional stream header contained an unknown stream type.

Reviewer comments:
Implements suggestions from
Updated to replace HTTPLIMITEXCEEDED and HTTPPUSHIDERROR with the single error HTTPID_ERROR.
There are presently a few cases where the Push ID can be used incorrectly. The table shows current error codes, proposes replacing the HTTPDUPLICATEPUSH with an HTTPPUSHIDERROR code, and replaces use of HTTPMALFORMED frame. Note, on I suggested a slightly different solution to replace HTTPMALFORMED FRAME, but I think this is a better path.
To me it seems that having an error code for specific part of push (i.e. ID of push-related information being incorrect) a bit too detailed. I think it sticks out, because we do not have things like "IDERROR, STREAMIDERROR. Also, using PUSHIDERROR in some cases while using MALFORMEDFRAME in others seems confusing. I think we might want to consider doing either of the two: Use PUSHIDERROR (or a generic error code for push) for all the push-related errors. This makes sense if we consider push as an optional feature of HTTP/3 Introduce IDERROR that captures all the limit and ID errors.
Thanks NAME The proposal is a bit of a compromise, so I agree with some of the points that you make. I like your idea of replacing HTTPLIMITEXCEEDED with HTTPIDERROR. I think it solves the issue in the most general way and doesn't change the protocol mechanics. What follows is the response I made first, before coming to that agreement :) Right. The difference here is that H3 frames only reference the stream and placeholder ID spaces, they don't control them. So, if the peer references an ID that doesn't exist then respond with HTTPLIMITEXCEEDED. Multiple references to the same ID is a little different, its ok to reuse placeholder ID (as long as the above condition holds, and you create a semantically valid PRIORITY frame). The transport spec discusses reuse of stream IDs at that layer; in H3 we only use stream ID for PRIORITY and GOAWAY - this has similar restrictions. However, server push is a little different because it's ID space is managed by the MAXPUSHID frame. The three errors that use HTTPPUSHIDERROR are unique to push. However, if we modified HTTPLIMITEXCEEDED to cover exceeding limits and incorrect ID usage (call it HTTPID_ERROR) I think that would work.
I updated the PR with the above, so that we can see what it looks like.
I'm not totally sold on this, but I'm having trouble articulating what it is that doesn't flow for me. I suspect that means I don't actually disagree, I just need some time to wrap my head around it.
We merged so I'm closing this as completed.

Revised text:
server can promise.  A server SHOULD use Push IDs sequentially, starting at 0.  A client MUST treat receipt of a push stream with a Push ID that is greater than the maximum Push ID as a connection  <ins> error of type HTTP_ID_ERROR. </ins> The header of the request message is carried by a PUSH_PROMISE frame (see frame-push-promise) on the request stream which generated the Each Push ID MUST only be used once in a push stream header.  If a push stream header includes a Push ID that was used in another push stream header, the client MUST treat this as a connection error of  <ins> type HTTP_ID_ERROR. </ins> 6.2.3. A PRIORITY frame that references a non-existent Push ID, a Placeholder ID greater than the server's limit, or a Stream ID the client is not yet permitted to open MUST be treated as a connection  <ins> error of type HTTP_ID_ERROR. </ins> A PRIORITY frame received on any stream other than the control stream MUST be treated as a connection error of type HTTP_WRONG_STREAM. See QPACK for more details. A server MUST NOT use a Push ID that is larger than the client has  <ins> provided in a MAX_PUSH_ID frame (frame-max-push-id).  A client MUST treat receipt of a PUSH_PROMISE frame that contains a larger Push ID than the client has advertised as a connection error of HTTP_ID_ERROR. A server MUST NOT use the same Push ID in multiple PUSH_PROMISE frames.  A client MUST treat receipt of a Push ID which has already been promised as a connection error of type HTTP_ID_ERROR. </ins> If a PUSH_PROMISE frame is received on the control stream, the client MUST respond with a connection error (errors) of type (see frame-push-promise).  A MAX_PUSH_ID frame cannot reduce the maximum Push ID; receipt of a MAX_PUSH_ID that contains a smaller value than previously received MUST be treated as a connection error  <ins> of type HTTP_ID_ERROR. </ins> 7.2.9. Push ID that is larger than the client has provided in a MAX_PUSH_ID frame (frame-max-push-id).  A client MUST treat receipt of a DUPLICATE_PUSH that contains a larger Push ID than the client has  <ins> advertised as a connection error of type HTTP_ID_ERROR. </ins> This frame allows the server to use the same server push in response to multiple concurrent requests.  Referencing the same server push A frame was received on a stream where it is not permitted.  <ins> A Stream ID, Push ID, or Placeholder ID was used incorrectly, such as exceeding a limit, reducing a limit, or being reused. </ins>  <ins> N/A </ins> A unidirectional stream header contained an unknown stream type.