{"record":{"id":"4786850b1a74ad2d","repo":"denoland/deno","slug":"err-http2-invalid-stream","errorCode":"ERR_HTTP2_INVALID_STREAM","errorMessage":"The stream has been destroyed","messagePattern":"The stream has been destroyed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":2466,"sourceCode":"    if (this.destroyed) {\n      // deno-lint-ignore deno-internal/prefer-primordials\n      this.push(null);\n      return;\n    }\n    if (!this[kState].didRead) {\n      this._readableState.readingMore = false;\n      this[kState].didRead = true;\n    }\n    if (!this.pending) {\n      FunctionPrototypeCall(streamOnResume, this);\n    } else {\n      this.once(\"ready\", streamOnResume);\n    }\n  }\n\n  sendTrailers(headers) {\n    if (this.destroyed || this.closed) {\n      throw new ERR_HTTP2_INVALID_STREAM();\n    }\n    if (this[kSentTrailers]) {\n      throw new ERR_HTTP2_TRAILERS_ALREADY_SENT();\n    }\n    if (!this[kState].trailersReady) {\n      throw new ERR_HTTP2_TRAILERS_NOT_READY();\n    }\n\n    assertIsObject(headers, \"headers\");\n    headers = ObjectAssign({ __proto__: null }, headers);\n\n    debugStreamObj(this, \"sending trailers\");\n\n    this[kUpdateTimer]();\n\n    const headersList = buildNgHeaderString(\n      headers,\n      assertValidPseudoHeaderTrailer,","sourceCodeStart":2448,"sourceCodeEnd":2484,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L2448-L2484","documentation":"http2stream.sendTrailers(headers) throws ERR_HTTP2_INVALID_STREAM ('The stream has been destroyed') as its first guard when stream.destroyed or stream.closed is true. Trailers are trailing HEADERS frames and can only be sent on a live (half-closed) stream; once the peer sent RST_STREAM or the stream closed, there is nothing to attach them to.","triggerScenarios":"Calling sendTrailers after the client aborted (RST_STREAM) mid-request; awaiting an async operation (db commit, hash) inside the 'wantTrailers' handler that resolves after the stream closed; sending trailers from a 'close' handler.","commonSituations":"Servers computing checksum/metadata trailers from a slow source while an impatient client times out and resets the stream; shared trailer-sending helpers invoked in finally blocks; retries that race stream teardown.","solutions":["Check liveness before sending: if (!stream.destroyed && !stream.closed) stream.sendTrailers(headers)","Send trailers only from inside the 'wantTrailers' listener, which only fires while the stream is alive","Wrap the send in try/catch and treat ERR_HTTP2_INVALID_STREAM as a benign lost-stream race"],"exampleFix":"// before\nstream.on('wantTrailers', async () => {\n  const checksum = await hashBody(); // client may abort meanwhile\n  stream.sendTrailers({ 'x-checksum': checksum }); // throws if closed\n});\n\n// after\nstream.on('wantTrailers', async () => {\n  const checksum = await hashBody();\n  if (!stream.destroyed && !stream.closed) {\n    stream.sendTrailers({ 'x-checksum': checksum });\n  }\n});","handlingStrategy":"validation","validationCode":"const sendTrailersIfLive = (stream: Http2Stream, headers: Record<string, string>) => {\n  if (!stream.destroyed && !stream.closed) {\n    stream.sendTrailers(headers);\n    return true;\n  }\n  return false;\n};","typeGuard":"const isLiveHttp2Stream = (s: Http2Stream): boolean =>\n  !s.destroyed && !s.closed;","tryCatchPattern":"try {\n  stream.sendTrailers(headers);\n} catch (err) {\n  if ((err as NodeJS.ErrnoException).code === 'ERR_HTTP2_INVALID_STREAM') {\n    // client reset the stream; the trailers are simply lost — safe to ignore\n  } else throw err;\n}","preventionTips":["Send trailers only from the 'wantTrailers' listener","Treat client aborts as expected: check stream liveness after any await","Drop references to streams on their 'close' event"],"tags":["http2","trailers","stream-lifecycle","node-compat"],"backgroundTag":"stream-already-destroyed","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}