denoland/deno · error · NodeRangeError

ERR_HTTP2_INFO_STATUS_NOT_ALLOWED

ERR_HTTP2_INFO_STATUS_NOT_ALLOWED

Error message

Informational status codes cannot be used

What it means

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.

Source

Thrown at ext/node/polyfills/internal/http2/compat.js:609

    return this[kStream].writableFinished;
  }

  get writableLength() {
    return this[kStream].writableLength;
  }

  get writableObjectMode() {
    return this[kStream].writableObjectMode;
  }

  get writableNeedDrain() {
    return this[kStream].writableNeedDrain;
  }

  set statusCode(code) {
    code |= 0;
    if (code >= 100 && code < 200) {
      throw new ERR_HTTP2_INFO_STATUS_NOT_ALLOWED();
    }
    if (code < 100 || code > 599) {
      throw new ERR_HTTP2_STATUS_INVALID(code);
    }
    this[kState].statusCode = code;
  }

  setTrailer(name, value) {
    validateString(name, "name");
    name = StringPrototypeToLowerCase(StringPrototypeTrim(name));
    assertValidHeader(name, value);
    this[kTrailers][name] = value;
  }

  addTrailers(headers) {
    const keys = ObjectKeys(headers);
    let key = "";
    for (let i = 0; i < keys.length; i++) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Only assign statusCode for final statuses (200-599); skip 1xx when forwarding
  2. For 103 Early Hints use response.writeEarlyHints() (Node >= 18.3) or skip the optimization
  3. Clamp when unsure: response.statusCode = status >= 200 ? status : 200

Example fix

// before
if (upstreamStatus) response.statusCode = upstreamStatus; // 103 throws

// after
if (upstreamStatus >= 200) response.statusCode = upstreamStatus;
else if (upstreamStatus === 103) response.writeEarlyHints({ link: preloadLink });
Defensive patterns

Strategy: validation

Validate before calling

function isFinalStatus(code) {
  return Number.isInteger(code) && code >= 200 && code <= 599;
}
if (isFinalStatus(upstreamStatus)) res.statusCode = upstreamStatus;
else if (upstreamStatus === 103) res.writeEarlyHints({ link: preloadLink });

Type guard

function isSettableStatusCode(code: unknown): code is number {
  return Number.isInteger(code) && code >= 200 && code <= 599;
}

Try / catch

try {
  res.statusCode = s;
} catch (err) {
  if (err.code === "ERR_HTTP2_INFO_STATUS_NOT_ALLOWED") return; // drop 1xx silently
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/4449705d316bdd02. Report an issue: GitHub.