denoland/deno · error · Error

ERR_HTTP2_UNSUPPORTED_PROTOCOL

ERR_HTTP2_UNSUPPORTED_PROTOCOL

Error message

protocol "${protocol}" is unsupported.

What it means

http2.connect(authority) maps the authority's URL scheme onto a transport: 'https:' creates a TLS socket via tls.connect, 'http:' a plaintext TCP socket via net.connect. The switch's default branch throws ERR_HTTP2_UNSUPPORTED_PROTOCOL for every other scheme. The scheme comes from the authority URL first, then options.protocol, then defaults to 'https:' — so the error requires an explicitly non-http(s) scheme.

Source

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

  }

  let socket;
  if (typeof options.createConnection === "function") {
    socket = options.createConnection(authority, options);
  } else {
    switch (protocol) {
      case "http:":
        socket = net.connect({ port, host, ...options });
        break;
      case "https:":
        socket = tls.connect(
          port,
          host,
          initializeTLSOptions(options, net.isIP(host) ? undefined : host),
        );
        break;
      default:
        throw new ERR_HTTP2_UNSUPPORTED_PROTOCOL(protocol);
    }
  }

  const session = new ClientHttp2Session(options, socket);

  session[kAuthority] = `${options.servername || host}:${port}`;
  session[kProtocol] = protocol;

  if (typeof listener === "function") {
    session.once("connect", listener);
  }

  return session;
}

// Support util.promisify
const promisifyConnect = function (authority, options) {
  return new Promise((resolve, reject) => {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Use https://example.com for TLS HTTP/2 or http://example.com for plaintext (h2c) — h2 over TLS is implied by https:.
  2. Convert gRPC targets: 'h2://host:port' becomes 'https://host:port'.
  3. Normalize the authority URL (or set options.protocol) to http:/https: before calling connect; fail fast in config validation for anything else.

Example fix

// before
const session = http2.connect('h2://example.com:443'); // ERR_HTTP2_UNSUPPORTED_PROTOCOL

// after
const session = http2.connect('https://example.com:443');
Defensive patterns

Strategy: validation

Validate before calling

function normalizeHttp2Target(authority) {
  const u = typeof authority === 'string' ? new URL(authority) : authority;
  if (u.protocol !== 'https:' && u.protocol !== 'http:') {
    throw new RangeError(`http2.connect supports http:/https:, got ${u.protocol}`);
  }
  return u;
}
const session = http2.connect(normalizeHttp2Target(target));

Type guard

function isSupportedHttp2Protocol(u) {
  return u instanceof URL && (u.protocol === 'https:' || u.protocol === 'http:');
}

Try / catch

try {
  const session = http2.connect(authority);
} catch (e) {
  if (e.code === 'ERR_HTTP2_UNSUPPORTED_PROTOCOL') {
    // normalize scheme (e.g. h2:// -> https://) and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: http2.connect('h2://example.com:443') (h2 is not an accepted scheme here); a URL object whose protocol is 'grpc:', 'unix:', or 'tcp:'; passing options.protocol = 'h2:' with a scheme-less authority.

Common situations: gRPC-style targets copied from grpc client configs ('h2://host', 'dns:///host'); Unix-domain-socket URLs; hand-built authority strings with invented schemes; uppercase schemes are fine (URL normalizes to lowercase).

Related errors


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