{"record":{"id":"d392bc718fcf9b59","repo":"denoland/deno","slug":"err-http2-headers-after-respond","errorCode":"ERR_HTTP2_HEADERS_AFTER_RESPOND","errorMessage":"Cannot specify additional headers after response initiated","messagePattern":"Cannot specify additional headers after response initiated","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":3449,"sourceCode":"        headers,\n        streamOptions,\n      ),\n    );\n  }\n\n  // Sends a block of informational headers. In theory, the HTTP/2 spec\n  // allows sending a HEADER block at any time during a streams lifecycle,\n  // but the HTTP request/response semantics defined in HTTP/2 places limits\n  // such that HEADERS may only be sent *before* or *after* DATA frames.\n  // If the block of headers being sent includes a status code, it MUST be\n  // a 1xx informational code and it MUST be sent before the request/response\n  // headers are sent, or an error will be thrown.\n  additionalHeaders(headers) {\n    if (this.destroyed || this.closed) {\n      throw new ERR_HTTP2_INVALID_STREAM();\n    }\n    if (this.headersSent) {\n      throw new ERR_HTTP2_HEADERS_AFTER_RESPOND();\n    }\n\n    assertIsObject(headers, \"headers\");\n    headers = ObjectAssign({ __proto__: null }, headers);\n\n    debugStreamObj(this, \"sending additional headers\");\n\n    if (headers[HTTP2_HEADER_STATUS] != null) {\n      const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;\n      if (statusCode === HTTP_STATUS_SWITCHING_PROTOCOLS) {\n        throw new ERR_HTTP2_STATUS_101();\n      }\n      if (statusCode < 100 || statusCode >= 200) {\n        throw new ERR_HTTP2_INVALID_INFO_STATUS(headers[HTTP2_HEADER_STATUS]);\n      }\n    }\n\n    this[kUpdateTimer]();","sourceCodeStart":3431,"sourceCodeEnd":3467,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L3431-L3467","documentation":"additionalHeaders() may only send informational headers *before* the final response headers. Once headersSent is true (respond/respondWithFile/respondWithFD already ran), calling it throws ERR_HTTP2_HEADERS_AFTER_RESPOND — HEADERS after DATA violate request/response semantics in HTTP/2.","triggerScenarios":"Sending 103 Early Hints after already calling respond(); a hint middleware ordered after the responding handler; sending trailer-style headers via additionalHeaders instead of the waitForTrailers mechanism.","commonSituations":"Middleware pipelines where hint emission happens too late; confusion between informational headers (before response) and trailers (after body, which need options.waitForTrailers on respond).","solutions":["Guard with if (stream.headersSent) return; before additionalHeaders()","Emit hints at the very start of the request lifecycle, before any heavy work","For post-body headers use trailers: respond(headers, { waitForTrailers: true }) and stream.on('wantTrailers', ...)","Reorder middleware so hints precede the responder"],"exampleFix":"// before\nstream.respond({ \":status\": 200 });\nstream.end(body);\nstream.additionalHeaders({ \":status\": 103, link: \"</a.js>; rel=preload\" });\n\n// after\nif (!stream.headersSent) {\n  stream.additionalHeaders({ \":status\": 103, link: \"</a.js>; rel=preload\" });\n}\nstream.respond({ \":status\": 200 });\nstream.end(body);","handlingStrategy":"validation","validationCode":"if (stream.headersSent) {\n  return; // final response already started; hints no longer allowed\n}\nstream.additionalHeaders(hints);","typeGuard":"function canSendInfo(stream) {\n  return !stream.headersSent && !stream.destroyed && !stream.closed;\n}","tryCatchPattern":"try {\n  stream.additionalHeaders(hints);\n} catch (err) {\n  if (err.code === \"ERR_HTTP2_HEADERS_AFTER_RESPOND\") return; // too late for hints\n  throw err;\n}","preventionTips":["Order hint middleware before anything that can respond","For after-body metadata use trailers (waitForTrailers + 'wantTrailers'), not additionalHeaders"],"tags":["http2","node-compat","early-hints","headers-sent","deno"],"backgroundTag":"response-already-sent","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}