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
- Use https://example.com for TLS HTTP/2 or http://example.com for plaintext (h2c) — h2 over TLS is implied by https:.
- Convert gRPC targets: 'h2://host:port' becomes 'https://host:port'.
- 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
- Normalize every upstream target URL to http:/https: at config-load time.
- Never invent schemes like h2:// or h2c:// for http2.connect — the scheme selects the transport; h2 is implied.
- Reject unknown schemes during config validation instead of at connection time.
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.