denoland/deno · error · NodeTypeError

ERR_HTTP2_INVALID_HEADER_VALUE

ERR_HTTP2_INVALID_HEADER_VALUE

Error message

Invalid value "${value}" for header "${name}"

What it means

assertValidHeader in http2 compat also checks the value: a header value of undefined or null throws ERR_HTTP2_INVALID_HEADER_VALUE, because HTTP/2 cannot encode a header with no value. Every header set through setHeader/appendHeader/setTrailer/writeHead must carry an actual value (string, number, or array).

Solutions

  1. Skip the header entirely when the value is null or undefined
  2. Default it when an empty value is meaningful: setHeader(name, value ?? "")
  3. Filter header maps before writeHead: Object.fromEntries(Object.entries(h).filter(([, v]) => v != null))

Example fix

// before
for (const [k, v] of Object.entries(config.headers)) res.setHeader(k, v); // undefined v throws

// after
for (const [k, v] of Object.entries(config.headers)) {
  if (v !== undefined && v !== null) res.setHeader(k, v);
}
Defensive patterns

Strategy: validation

Validate before calling

function hasHeaderValue(v) {
  return v !== undefined && v !== null;
}
for (const [k, v] of Object.entries(headers)) {
  if (hasHeaderValue(v)) res.setHeader(k, v);
}

Type guard

function isDefiniteHeaderValue(
  v: unknown,
): v is string | number | string[] | number[] {
  return v !== undefined && v !== null;
}

Try / catch

try {
  res.setHeader(k, v);
} catch (err) {
  if (err.code === "ERR_HTTP2_INVALID_HEADER_VALUE" && v == null) return; // just skip
  throw err;
}

Prevention

When it happens

Trigger: res.setHeader("x-optional", undefined) from optional config or destructuring; object-spread headers containing undefined fields passed to writeHead(); trailer values fetched from a Map/lookup that returned null or undefined.

Common situations: Optional feature flags: setHeader("x-deprecated", flags.deprecated) where the field is absent; spreads like { ...commonHeaders, ...overrides } where some keys are undefined; JSON config with null values fed straight into headers.

Related errors


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

Appendix: source

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

let statusConnectionHeaderWarned = false;

// Defines and implements an API compatibility layer on top of the core
// HTTP/2 implementation, intended to provide an interface that is as
// close as possible to the current require('http') API

const assertValidHeader = hideStackFrames((name, value) => {
  if (
    name === "" ||
    typeof name !== "string" ||
    StringPrototypeIncludes(name, " ")
  ) {
    throw new ERR_INVALID_HTTP_TOKEN.HideStackFramesError("Header name", name);
  }
  if (isPseudoHeader(name)) {
    throw new ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED.HideStackFramesError();
  }
  if (value === undefined || value === null) {
    throw new ERR_HTTP2_INVALID_HEADER_VALUE.HideStackFramesError(value, name);
  }
  if (!isConnectionHeaderAllowed(name, value)) {
    connectionHeaderMessageWarn();
  }
});

function isPseudoHeader(name) {
  switch (name) {
    case HTTP2_HEADER_STATUS: // :status
    case HTTP2_HEADER_METHOD: // :method
    case HTTP2_HEADER_PATH: // :path
    case HTTP2_HEADER_AUTHORITY: // :authority
    case HTTP2_HEADER_SCHEME: // :scheme
      return true;
    default:
      return false;
  }
}

View on GitHub (pinned to 9ad36f7a2c)