denoland/deno · error · TypeError

ERR_HTTP2_INVALID_ORIGIN

ERR_HTTP2_INVALID_ORIGIN

Error message

HTTP/2 ORIGIN frames require a valid origin

What it means

Http2Session.origin() validates every entry in origins: strings are converted with getURLOrigin(), objects contribute their .origin, and validateString(origin) enforces a string. If the resulting origin is the literal 'null' — the URL parser's opaque-origin marker for schemes like file: or data: — ERR_HTTP2_INVALID_ORIGIN is thrown, since an ORIGIN frame requires concrete serialized origins.

Source

Thrown at ext/node/polyfills/http2.ts:4656

    }

    if (origins.length === 0) {
      return;
    }

    let arr = "";
    let len = 0;
    const count = origins.length;
    for (let i = 0; i < count; i++) {
      let origin = origins[i];
      if (typeof origin === "string") {
        origin = getURLOrigin(origin);
      } else if (origin != null && typeof origin === "object") {
        origin = origin.origin;
      }
      validateString(origin, "origin");
      if (origin === "null") {
        throw new ERR_HTTP2_INVALID_ORIGIN();
      }

      arr += `${origin}\0`;
      len += origin.length;
    }

    if (len > kMaxALTSVC) {
      throw new ERR_HTTP2_ORIGIN_LENGTH();
    }

    this[kHandle].origin(arr, count);
  }
}

// ClientHttp2Session instances have to wait for the socket to connect after
// they have been created. Various operations such as request() may be used,
// but the actual protocol communication will only occur after the socket
// has been connected.

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass absolute https:// URLs (ORIGIN frames are only meaningful on TLS) such as 'https://example.com'
  2. Filter the list first: origins.filter((o) => o && o !== 'null') on the string forms
  3. Derive entries from the server's own certificate/hostname configuration, not from request paths

Example fix

// before
session.origin('file:///srv/app', 'https://example.com');

// after
session.origin('https://example.com', 'https://cdn.example.com');
Defensive patterns

Strategy: validation

Validate before calling

const valid = origins.map((o) => typeof o === 'string' ? new URL(o).origin : o?.origin);
if (valid.some((o) => typeof o !== 'string' || o === 'null')) {
  throw new TypeError('every origin must be a concrete https origin');
}
session.origin(...origins);

Type guard

function areConcreteOrigins(origins) {
  return origins.every((o) => {
    const org = typeof o === 'string' ? new URL(o).origin : o?.origin;
    return typeof org === 'string' && org !== 'null' && org.length > 0;
  });
}

Prevention

When it happens

Trigger: session.origin('file:///srv/site'), session.origin({ origin: 'null' }), or any entry whose URL string has no hierarchical http/https origin.

Common situations: Building the origin list from config paths or file:// mounts instead of https:// base URLs; mixing relative paths into the list (those throw ERR_INVALID_URL even earlier via getURLOrigin); opaque origins copied from browser-style URL objects.

Related errors


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