{"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":"error_code","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_client.js","lineNumber":567,"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":549,"sourceCodeEnd":585,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/_http_client.js#L549-L585","documentation":"ClientRequest compares options.protocol (or the agent's protocol) against the selected agent's protocol and requires them to match: an https: request needs an agent whose protocol is 'https:', an http: request one with 'http:'. A mismatch throws ERR_INVALID_PROTOCOL before any socket is opened, because the agent would otherwise speak the wrong TLS/plain scheme.","triggerScenarios":"https.request(url, { agent: http.globalAgent }); http.request(url, { agent: new https.Agent() }); a custom Agent class whose default protocol disagrees with options.protocol; manually setting protocol: 'https:' while the default agent is the http one.","commonSituations":"Sharing one keepAlive agent instance across http and https modules in an app; global agent injection in libraries (axios-style defaults) forgetting scheme-specific agents; proxy agents created with http.Agent used for https targets.","solutions":["Use the matching module's agent: https.globalAgent / new https.Agent() for https requests","Keep one agent per scheme and select by protocol: const agent = protocol === 'https:' ? httpsAgent : httpAgent","Drop the agent option entirely and let each module use its own default"],"exampleFix":"// before\nhttps.request(url, { agent: http.globalAgent }, cb); // ERR_INVALID_PROTOCOL\n\n// after\nimport http from 'node:http';\nimport https from 'node:https';\nconst agents = {\n  'http:': new http.Agent({ keepAlive: true }),\n  'https:': new https.Agent({ keepAlive: true }),\n};\nconst u = new URL(url);\nhttps.request(u, { agent: agents[u.protocol] }, cb);","handlingStrategy":"validation","validationCode":"import http from 'node:http';\nimport https from 'node:https';\nconst agents = {\n  'http:': new http.Agent({ keepAlive: true }),\n  'https:': new https.Agent({ keepAlive: true }),\n};\nfunction agentFor(protocol) {\n  const a = agents[protocol];\n  if (!a) throw new Error(`no agent for ${protocol}`);\n  return a;\n}","typeGuard":"const agentMatchesProtocol = (agent, protocol) =>\n  !agent || !agent.protocol || agent.protocol === protocol;","tryCatchPattern":"try { https.request(url, { agent }, cb); }\ncatch (err) {\n  if (err.code === 'ERR_INVALID_PROTOCOL') {\n    return https.request(url, { agent: https.globalAgent }, cb);\n  }\n  throw err;\n}","preventionTips":["Keep one agent per scheme; select it from the URL's protocol","Never pass http.globalAgent to https.request or vice versa, including in library defaults"],"tags":["http","https","protocol","agent","node-compat","deno"],"backgroundTag":"protocol-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}