denoland/deno · error · NodeRangeError

ERR_HTTP2_STATUS_INVALID

ERR_HTTP2_STATUS_INVALID

Error message

Invalid status code: ${code}

What it means

The same statusCode setter throws ERR_HTTP2_STATUS_INVALID for any value outside 100-599 after the bitwise `code |= 0` coercion. Values like 99, 600, or strings coercing to 0/NaN fail; fractional numbers are silently truncated by the coercion before the range check.

Source

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

  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++) {
      key = keys[i];
      this.setTrailer(key, headers[key]);
    }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Validate with Number.isInteger(code) && code >= 100 && code <= 599 before assigning
  2. Map non-HTTP internal codes to a real class (usually 500) plus a machine-readable body field
  3. Parse statuses from external input with parseInt and a range check

Example fix

// before
res.statusCode = err.code; // err.code = 6003 -> ERR_HTTP2_STATUS_INVALID

// after
res.statusCode = err.httpStatus ?? 500; // map internal codes to real HTTP statuses
Defensive patterns

Strategy: validation

Validate before calling

if (!(Number.isInteger(s) && s >= 100 && s <= 599)) s = 500;
res.statusCode = s;

Type guard

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

Try / catch

try {
  res.statusCode = s;
} catch (err) {
  if (err.code === "ERR_HTTP2_STATUS_INVALID") res.statusCode = 500;
  else throw err;
}

Prevention

When it happens

Trigger: response.statusCode = 600 (custom 'edge' code); statusCode = "ok" or "404?" coercing to NaN/0; computed statuses (base + offset) slipping past 599; truthy-but-invalid values from untyped config.

Common situations: Internal application error codes above 599 leaking into responses; statuses parsed from headers/query as strings without conversion; off-by-one errors in status mapping tables.

Related errors


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