{"record":{"id":"c92951a0bee61c90","repo":"denoland/deno","slug":"err-invalid-protocol","errorCode":"ERR_INVALID_PROTOCOL","errorMessage":"Protocol \"${protocol}\" not supported. Expected \"${expectedProtocol}\"","messagePattern":"Protocol \"(.+?)\" not supported\\. Expected \"(.+?)\"","errorType":"validation","errorClass":"NodeTypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_client.js","lineNumber":513,"sourceCode":"    );\n  }\n  this.agent = agent;\n\n  const protocol = options.protocol || defaultAgent.protocol;\n  let expectedProtocol = defaultAgent.protocol;\n  if (this.agent?.protocol) {\n    expectedProtocol = this.agent.protocol;\n  }\n\n  if (options.path) {\n    const path = String(options.path);\n    if (INVALID_PATH_REGEX.test(path)) {\n      throw new ERR_UNESCAPED_CHARACTERS(\"Request path\");\n    }\n  }\n\n  if (protocol !== expectedProtocol) {\n    throw new ERR_INVALID_PROTOCOL(protocol, expectedProtocol);\n  }\n\n  const defaultPort = options.defaultPort ||\n    (this.agent?.defaultPort);\n\n  const optsWithoutSignal = { __proto__: null, ...options };\n\n  // The `_proxy*` fields are internal transport details set only by the proxy\n  // selection below. A caller must not be able to supply them directly: doing\n  // so would route the request through an arbitrary proxy while bypassing the\n  // target permission check that the proxy branch performs. Strip any that came\n  // in via `options` so only the values computed here are honored.\n  delete optsWithoutSignal._proxy;\n  delete optsWithoutSignal._proxyTargetHost;\n  delete optsWithoutSignal._proxyTargetPort;\n  delete optsWithoutSignal._proxyProtocol;\n  delete optsWithoutSignal._proxyUseProxyConnection;\n","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_http_client.js#L495-L531","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst agent = new http.Agent({ keepAlive: true });\nconst req = https.request('https://api.test/x', { agent });\n\n// after\nconst agent = new https.Agent({ keepAlive: true });\nconst req = https.request('https://api.test/x', { agent });","handlingStrategy":"validation","validationCode":"const target = new URL(urlStr);\nconst mod = target.protocol === 'https:' ? https : http;\nif (agent && agent.protocol && agent.protocol !== target.protocol) {\n  throw new Error(`agent protocol ${agent.protocol} != ${target.protocol}`);\n}\nmod.request(target, { agent });","typeGuard":"const protocolsMatch = (reqProtocol, agent) => !agent?.protocol || agent.protocol === reqProtocol;","tryCatchPattern":"try { https.request(url, { agent }); } catch (e) { if (e.code === 'ERR_INVALID_PROTOCOL') { /* rebuild agent from the https module */ } else throw e; }","preventionTips":["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"],"tags":["http","https","agent","protocol","node-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}