denoland/deno · error · NodeTypeError
ERR_INVALID_PROTOCOL
ERR_INVALID_PROTOCOL
Error message
Protocol "${protocol}" not supported. Expected "${expectedProtocol}" What it means
ClientRequest derives expectedProtocol from the agent (this.agent?.protocol, falling back to the default agent's) and compares it with the effective protocol from options.protocol or the default. A mismatch — most commonly https: with the http module's globalAgent — throws ERR_INVALID_PROTOCOL. The check runs after path validation, before any connection is made.
Source
Thrown at ext/node/polyfills/_http_client.js:513
);
}
this.agent = agent;
const protocol = options.protocol || defaultAgent.protocol;
let expectedProtocol = defaultAgent.protocol;
if (this.agent?.protocol) {
expectedProtocol = this.agent.protocol;
}
if (options.path) {
const path = String(options.path);
if (INVALID_PATH_REGEX.test(path)) {
throw new ERR_UNESCAPED_CHARACTERS("Request path");
}
}
if (protocol !== expectedProtocol) {
throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol);
}
const defaultPort = options.defaultPort ||
(this.agent?.defaultPort);
const optsWithoutSignal = { __proto__: null, ...options };
// The `_proxy*` fields are internal transport details set only by the proxy
// selection below. A caller must not be able to supply them directly: doing
// so would route the request through an arbitrary proxy while bypassing the
// target permission check that the proxy branch performs. Strip any that came
// in via `options` so only the values computed here are honored.
delete optsWithoutSignal._proxy;
delete optsWithoutSignal._proxyTargetHost;
delete optsWithoutSignal._proxyTargetPort;
delete optsWithoutSignal._proxyProtocol;
delete optsWithoutSignal._proxyUseProxyConnection;
View on GitHub (pinned to 89f33cbef2)
Solutions
- Use the matching module: https.request for https: targets, http.request for http:
- Construct the agent from the same module as the request: new https.Agent({ keepAlive: true })
- Omit the custom agent (or options.protocol) and let the default agent for the module you called supply the protocol
Example fix
// before
const agent = new http.Agent({ keepAlive: true });
const req = https.request('https://api.test/x', { agent });
// after
const agent = new https.Agent({ keepAlive: true });
const req = https.request('https://api.test/x', { agent }); Defensive patterns
Strategy: validation
Validate before calling
const target = new URL(urlStr);
const mod = target.protocol === 'https:' ? https : http;
if (agent && agent.protocol && agent.protocol !== target.protocol) {
throw new Error(`agent protocol ${agent.protocol} != ${target.protocol}`);
}
mod.request(target, { agent }); Type guard
const protocolsMatch = (reqProtocol, agent) => !agent?.protocol || agent.protocol === reqProtocol;
Try / catch
try { https.request(url, { agent }); } catch (e) { if (e.code === 'ERR_INVALID_PROTOCOL') { /* rebuild agent from the https module */ } else throw e; } Prevention
- Pick the module from the URL scheme: (new URL(u).protocol === 'https:' ? https : http)
- Keep separate agents per protocol; name them httpAgent/httpsAgent
- Omit the agent option unless you need custom pooling
When it happens
Trigger: https.get('https://svc', { agent: new http.Agent() }) or http.request('https://...') where the default agent is http's; explicitly setting options.protocol: 'https:' while using an agent constructed for http; wiring a shared agent across both modules.
Common situations: Importing http instead of https (or a shared keepAlive agent used for both); refactoring a client from http to https without updating the agent; test doubles that inject a fake agent with the wrong protocol string.
Related errors
- ERR_HTTP_TRAILER_INVALID
- Unsupported 'certFile' / 'keyFile' options provided: use 'ce
- Unsupported 'alpnProtocols' option provided. 'h2' and 'http/
- ERR_INVALID_ARG_TYPE
- ERR_INVALID_URL
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/c92951a0bee61c90.
Report an issue: GitHub.