Title: quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE) · Issue #65509 · nodejs/node · GitHub
Open Graph Title: quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE) · Issue #65509 · nodejs/node
X Title: quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE) · Issue #65509 · nodejs/node
Description: Following #65442, which made rejected request streams reset with H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing — one needs a small API decision. 1. Can...
Open Graph Description: Following #65442, which made rejected request streams reset with H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing ...
X Description: Following #65442, which made rejected request streams reset with H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing ...
Opengraph URL: https://github.com/nodejs/node/issues/65509
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"quic: emit RFC 9114 stream-closure error codes (H3_REQUEST_CANCELLED, H3_REQUEST_INCOMPLETE)","articleBody":"Following #65442, which made rejected request streams reset with `H3_REQUEST_REJECTED`, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing — one needs a small API decision.\n\n### 1. Cancellation — `H3_REQUEST_CANCELLED` (0x10c)\n\nWhen an endpoint gives up on a request it no longer wants (client aborting a request, or a server abandoning a response after starting it), the stream should be reset as *cancelled*. There are two gaps today:\n\n- **A cancel is two operations, with no single one.** Fully cancelling a bidirectional stream means aborting both halves — `RESET_STREAM` (\"I will not send more\") and `STOP_SENDING` (\"do not send me more\").\n- **The code is wrong.** Both default to `0n` (`H3_NO_ERROR`), so a cancel currently signals *\"no error\"* to the peer.\n\n```js\n// Today: two calls, and both default to H3_NO_ERROR (0) — the peer is told \"no error\"\nstream.resetStream();\nstream.stopSending();\n```\n\n[RFC 9114 §4.1.1](https://www.rfc-editor.org/rfc/rfc9114.html#section-4.1.1): *\"Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests\"*, and a server abandoning a response after partial processing *\"SHOULD abort its response stream with the error code H3_REQUEST_CANCELLED.\"* node neither defaults to `0x10c` nor exposes it.\n\n**Proposal:** a `stream.cancel([reason])` method that resets both halves with `H3_REQUEST_CANCELLED`. Role-agnostic (client-cancel and server-abandon use the same code); the existing `resetStream()`/`stopSending()` stay as low-level primitives.\n\n```js\n// Proposed: one call, resets both halves with H3_REQUEST_CANCELLED (0x10c)\nstream.cancel();\n```\n\nThis keeps the numeric code out of consumer code (e.g. a future h3 client in undici, #5471), matching how `node:http2` offers `stream.close(code)` and how `fetch`/`AbortController` sits a layer above.\n\n*Alternative considered:* expose a named `H3_REQUEST_CANCELLED` constant for the existing resets. Still two calls, and it pushes the code choice onto every caller:\n\n```js\n// Alternative: still two calls, caller supplies the code via an exposed constant\nstream.resetStream(\u003cQUIC_CONSTANTS>.H3_REQUEST_CANCELLED);\nstream.stopSending(\u003cQUIC_CONSTANTS>.H3_REQUEST_CANCELLED);\n```\n\n### 2. Incomplete request — `H3_REQUEST_INCOMPLETE` (0x10d)\n\n[RFC 9114 §4.1](https://www.rfc-editor.org/rfc/rfc9114.html#section-4.1): *\"If a client-initiated stream terminates without enough of the HTTP message to provide a complete response, the server SHOULD abort its response stream with the error code H3_REQUEST_INCOMPLETE.\"*\n\nThis needs no API — the HTTP/3 layer can emit it when a request stream closes before a complete message. Today the teardown uses a generic code (`H3_NO_ERROR` or `H3_INTERNAL_ERROR`), so the client can't distinguish \"the server errored\" from \"my request was incomplete.\" Emitting `0x10d` puts the signal where it belongs.","author":{"url":"https://github.com/trivenay","@type":"Person","name":"trivenay"},"datePublished":"2026-08-23T19:49:30Z","url":"https://github.com/nodejs/node/issues/65509"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:6c1f08cc-e4f3-e39a-a1ca-46bb865f481d |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A162:5463F:6840BB:8D1802:6AED2697 |
| html-safe-nonce | f314fc7d68479080da28070c5d0ac21fde3cfa0a60724bbe70a774d523ee6766 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMTYyOjU0NjNGOjY4NDBCQjo4RDE4MDI6NkFFRDI2OTciLCJ2aXNpdG9yX2lkIjoiODY3MTc1NDEzNzM5NDU1NDUxOSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | fec8c8c476418b2309ec7646613e045a508b8e65c2eecd158ab4e0811f9d0176 |
| hovercard-subject-tag | issue:5228769116 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/65509/issue_layout |
| twitter:image | https://opengraph.githubassets.com/0306b27aeee03114fc602dd1512dc3b8edeb642f8b081115537d93885855bafb/nodejs/node/issues/65509 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/0306b27aeee03114fc602dd1512dc3b8edeb642f8b081115537d93885855bafb/nodejs/node/issues/65509 |
| og:image:alt | Following #65442, which made rejected request streams reset with H3_REQUEST_REJECTED, two more RFC 9114 stream-closure codes are still not emitted. Filing to agree on direction before implementing ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | trivenay |
| hostname | github.com |
| expected-hostname | github.com |
| None | 9d95365dda0576efe9b71f45807595e396bd0ca9286fba5367a05048e6ddf8f2 |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 98b41fa4b5635ceb45a4c54320fced73817ff290 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width