{"record":{"id":"4449705d316bdd02","repo":"denoland/deno","slug":"err-http2-info-status-not-allowed","errorCode":"ERR_HTTP2_INFO_STATUS_NOT_ALLOWED","errorMessage":"Informational status codes cannot be used","messagePattern":"Informational status codes cannot be used","errorType":"validation","errorClass":"NodeRangeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/http2/compat.js","lineNumber":609,"sourceCode":"    return this[kStream].writableFinished;\n  }\n\n  get writableLength() {\n    return this[kStream].writableLength;\n  }\n\n  get writableObjectMode() {\n    return this[kStream].writableObjectMode;\n  }\n\n  get writableNeedDrain() {\n    return this[kStream].writableNeedDrain;\n  }\n\n  set statusCode(code) {\n    code |= 0;\n    if (code >= 100 && code < 200) {\n      throw new ERR_HTTP2_INFO_STATUS_NOT_ALLOWED();\n    }\n    if (code < 100 || code > 599) {\n      throw new ERR_HTTP2_STATUS_INVALID(code);\n    }\n    this[kState].statusCode = code;\n  }\n\n  setTrailer(name, value) {\n    validateString(name, \"name\");\n    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));\n    assertValidHeader(name, value);\n    this[kTrailers][name] = value;\n  }\n\n  addTrailers(headers) {\n    const keys = ObjectKeys(headers);\n    let key = \"\";\n    for (let i = 0; i < keys.length; i++) {","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/http2/compat.js#L591-L627","documentation":"The Http2ServerResponse.statusCode setter refuses 1xx codes (100-199) with ERR_HTTP2_INFO_STATUS_NOT_ALLOWED. Informational statuses in HTTP/2 are protocol-level events (100 Continue, 103 Early Hints) generated by the session stack; they cannot be set as the final response status the way they (mis)work in http1.","triggerScenarios":"response.statusCode = 100/101/103; middleware copying a forwarded 1xx status from an upstream hop into the outgoing response; early-hints implementations trying to emit 103 by assigning statusCode.","commonSituations":"Proxy chains where an intermediate stored an informational status; code written for http1 that sets 100/101 manually; attempting 103 Early Hints without the dedicated API.","solutions":["Only assign statusCode for final statuses (200-599); skip 1xx when forwarding","For 103 Early Hints use response.writeEarlyHints() (Node >= 18.3) or skip the optimization","Clamp when unsure: response.statusCode = status >= 200 ? status : 200"],"exampleFix":"// before\nif (upstreamStatus) response.statusCode = upstreamStatus; // 103 throws\n\n// after\nif (upstreamStatus >= 200) response.statusCode = upstreamStatus;\nelse if (upstreamStatus === 103) response.writeEarlyHints({ link: preloadLink });","handlingStrategy":"validation","validationCode":"function isFinalStatus(code) {\n  return Number.isInteger(code) && code >= 200 && code <= 599;\n}\nif (isFinalStatus(upstreamStatus)) res.statusCode = upstreamStatus;\nelse if (upstreamStatus === 103) res.writeEarlyHints({ link: preloadLink });","typeGuard":"function isSettableStatusCode(code: unknown): code is number {\n  return Number.isInteger(code) && code >= 200 && code <= 599;\n}","tryCatchPattern":"try {\n  res.statusCode = s;\n} catch (err) {\n  if (err.code === \"ERR_HTTP2_INFO_STATUS_NOT_ALLOWED\") return; // drop 1xx silently\n  throw err;\n}","preventionTips":["Never propagate 1xx from upstream into an outgoing final response","Use writeEarlyHints() for 103; do not emulate it with statusCode"],"tags":["http2","status-code","early-hints"],"backgroundTag":"invalid-status-code","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}