§5.4
Chapter 05 · HTTP3 Frame Processing and Request Response Semantics

§5.4Error Handling, Stream Resets, and Connection Termination Semantics

RFC 9114RFC 9000

Learning objective. Distinguish the two scopes of HTTP/3 failure (resetting a single stream versus tearing down the whole connection) and read the graceful-shutdown and error-code semantics that decide which one you are looking at.

Two scopes of failure #

HTTP/3 errors come in exactly two blast radii, and telling them apart is most of the diagnosis:

  • Stream errors end one request; the connection and every other stream keep going.
  • Connection errors end everything, via a QUIC CONNECTION_CLOSE carrying an HTTP/3 error code.

Both draw from the same HTTP/3 error-code space (constants.md §12), so the code alone does not tell you the scope — the frame that carries it does.

ServerClient(1) cancel one request — connectionsurvivesstream 8 ends, other streams keepflowing(2) graceful shutdownin-flight requests finish, then theconnection closes(3) fatal protocol errorRESET_STREAM +STOP_SENDING on stream 8 ·H3_REQUEST_CANCELLED(0x010c)GOAWAY (last stream ID it willprocess)CONNECTION_CLOSE ·H3_FRAME_UNEXPECTED(0x0105) — whole connection ends
Fig. 5.4-1Three ways an HTTP/3 exchange can end. Cancelling a request resets just that stream; GOAWAY starts a graceful shutdown that lets in-flight requests finish; a fatal protocol violation closes the whole connection.RFC 9114 §8, §5.2

Resetting a single stream #

Cancelling a request resets its stream and nothing else. Either side may do it (a client whose response is no longer of interest, or a server that cannot process the request) by abruptly terminating the stream ([RFC 9114 §4.1.2]): a RESET_STREAM on its own send side and a STOP_SENDING on its receive side (§4.1), carrying an HTTP/3 error code such as H3_REQUEST_CANCELLED (0x010c). The transport delivers the reset; the peer stops work on that stream and reclaims its resources; every other request continues. This is the common, benign case: a user navigating away mid-download produces exactly this.

Graceful shutdown with GOAWAY #

An endpoint that wants to stop without dropping in-flight work sends a GOAWAY frame on its control stream to begin a graceful shutdown ([RFC 9114 §5.2]). A server's GOAWAY carries a client-initiated bidirectional stream ID; a client's carries a push ID. The identifier draws an explicit line: "requests or pushes with the indicated identifier or greater are rejected" by the sender ([RFC 9114 §5.2]), and are therefore safe to retry on a fresh connection. Requests below it may already have been processed and are given the chance to finish (their outcome is known only when a response, reset, or lower GOAWAY arrives). The endpoint then closes, ideally with H3_NO_ERROR. This is how a server drains connections for a deploy or restart without failing active requests — the difference between a rolling update users never notice and a wave of reset connections.

Connection errors and critical streams #

Some failures are not survivable and take the whole connection down with a CONNECTION_CLOSE and an HTTP/3 error code ([RFC 9114 §8]):

  • Framing and protocol violations. An invalid frame sequence is H3_FRAME_UNEXPECTED (0x0105, §5.1); a malformed or misplaced SETTINGS is H3_SETTINGS_ERROR (0x0109); a missing SETTINGS as the first control-stream frame is H3_MISSING_SETTINGS (0x010a).
  • Closing a critical stream. The control stream and the QPACK encoder/decoder streams must stay open for the connection's life; closing one is H3_CLOSED_CRITICAL_STREAM (0x0104, [RFC 9114 §6.2.1]).

These map onto the QUIC error machinery of §2.5: the HTTP/3 code travels in a QUIC CONNECTION_CLOSE of type 0x1d (application), and you read it the same way you read a transport error: find the CONNECTION_CLOSE, read the code, look it up.

Note

A stream reset is not a connection error, and conflating them is a classic misdiagnosis. A trace full of H3_REQUEST_CANCELLED resets on individual streams is usually healthy application behavior (cancellations, timeouts, navigations); a single CONNECTION_CLOSE with H3_FRAME_UNEXPECTED is a bug. The scope, read from the carrying frame, matters more than the code.

In practice

editorial Wire GOAWAY into your deployment lifecycle: a server that just stops accepting connections mid-request produces user-visible failures, while one that sends GOAWAY and drains cleanly does not. On the debugging side, when an HTTP/3 connection "drops," first classify the ending: a per-stream RESET_STREAM (one request died), a GOAWAY-then-close (intentional drain), or a CONNECTION_CLOSE with a protocol error code (a real bug, often an interop mismatch, §13.2). The three have completely different causes and fixes.

Takeaways #

HTTP/3 fails at one of two scopes: a stream reset (one request, via RESET_STREAM/STOP_SENDING with a code like H3_REQUEST_CANCELLED) or a connection error (everything, via CONNECTION_CLOSE with a code like H3_FRAME_UNEXPECTED), with GOAWAY providing an orderly drain in between. The carrying frame, not the code, tells you the blast radius. §5.5 makes all of this concrete by building a small HTTP/3 client and watching its frames and errors on the wire.