denoland/deno · error · NodeTypeError

ERR_INVALID_ARG_TYPE

ERR_INVALID_ARG_TYPE

Error message

The "statusCode" argument must be of type integer [100, 999]. Received ${statusCode}

What it means

writeHead coerces statusCode with statusCode |= 0 and then requires the range 100..999. Values outside it throw ERR_INVALID_ARG_TYPE asking for 'integer [100, 999]' (Node's quirk: an out-of-range status uses the arg-type error code). NaN|0 and undefined|0 both become 0, so missing or unparseable statuses fail here too.

Source

Thrown at ext/node/polyfills/_http_server.js:509

  this._writeRaw(head, "ascii", cb);
};

ServerResponse.prototype._implicitHeader = function _implicitHeader() {
  this.writeHead(this.statusCode);
};

ServerResponse.prototype.writeHead = function writeHead(
  statusCode,
  reason,
  obj,
) {
  if (this._header) {
    throw new ERR_HTTP_HEADERS_SENT("write");
  }

  statusCode |= 0;
  if (statusCode < 100 || statusCode > 999) {
    throw new ERR_INVALID_ARG_TYPE(
      "statusCode",
      "integer [100, 999]",
      statusCode,
    );
  }

  if (typeof reason === "string") {
    this.statusMessage = reason;
  } else {
    this.statusMessage ||= STATUS_CODES[statusCode] || "unknown";
    obj ??= reason;
  }
  this.statusCode = statusCode;

  // Enforce no body for 204 and 304 responses
  if (statusCode === 204 || statusCode === 304) {
    this._hasBody = false;
  }

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Validate the status is an integer in [100, 999] before calling writeHead, defaulting to 200 otherwise
  2. Fix the producer of the bad value (config parsing, Number() on user input)
  3. Never forward raw Number(userInput) results into the status
  4. Whitelist allowed statuses per endpoint

Example fix

// before
const code = Number(req.query.status); // NaN or out of range
res.writeHead(code);

// after
const parsed = Number(req.query.status);
const code = Number.isInteger(parsed) && parsed >= 100 && parsed <= 999 ? parsed : 200;
res.writeHead(code);
Defensive patterns

Strategy: validation

Validate before calling

function validStatusCode(code) {
  return Number.isInteger(code) && code >= 100 && code <= 999;
}
const status = validStatusCode(raw) ? raw : 200;
res.writeHead(status);

Type guard

function isStatusCode(v) {
  return typeof v === 'number' && Number.isInteger(v) && v >= 100 && v <= 999;
}

Try / catch

try {
  res.writeHead(code);
} catch (e) {
  if (e.code === 'ERR_INVALID_ARG_TYPE' && /statusCode/.test(e.message)) {
    res.writeHead(200);
  } else throw e;
}

Prevention

When it happens

Trigger: res.writeHead(99), res.writeHead(1000), res.writeHead(NaN), res.writeHead(undefined), or res.writeHead('abc') (coerces to 0); a status computed from config, headers, or query params without validation.

Common situations: Status read from a config file or proxied header (Number(req.headers['x-status']) producing NaN); typos like 2000; a missing value defaulting to 0 instead of 200; arithmetic on optional fields yielding NaN.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/03056f6bb3f4c264. Report an issue: GitHub.