{"record":{"id":"9cca0b286898ab5d","repo":"denoland/deno","slug":"err-invalid-url","errorCode":"ERR_INVALID_URL","errorMessage":"Invalid URL: ${path}","messagePattern":"Invalid URL: (.+?)","errorType":"error_code","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_client.js","lineNumber":492,"sourceCode":"  // An absolute-form path is already a complete target. It is only honored\n  // when its authority matches the one that `--allow-net` validated and it\n  // carries no userinfo: the permission check upstream sees only `host` and\n  // `port`, so an absolute `path` aimed at another authority (or embedding\n  // credentials for one) would otherwise retarget the proxied request past\n  // that check. Fail closed instead - like a denied direct connection.\n  let absolute = null;\n  try {\n    absolute = new URL(path);\n  } catch {\n    // Not absolute-form; resolved as an origin-form path below.\n  }\n  if (absolute !== null) {\n    if (\n      absolute.host !== base.host ||\n      absolute.username !== \"\" ||\n      absolute.password !== \"\"\n    ) {\n      throw new ERR_INVALID_URL(path);\n    }\n    return absolute.href;\n  }\n\n  try {\n    return new URL(base.origin + path).href;\n  } catch {\n    const portSuffix = +port === 80 ? \"\" : `:${port}`;\n    return `http://${authorityHost}${portSuffix}${path}`;\n  }\n}\n\nfunction ClientRequest(input, options, cb) {\n  FunctionPrototypeCall(OutgoingMessage, this);\n\n  if (typeof input === \"string\") {\n    const urlStr = input;\n    // Match Node: `new URL(...)` in ClientRequest surfaces as","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/_http_client.js#L474-L510","documentation":"When an HTTP proxy is configured, Deno rewrites the request path to absolute-form for the proxy hop (RFC 9112 section 3.2.2). buildProxyRequestTarget rejects an options.path that is itself an absolute URL aimed at a different authority than the host that passed the --allow-net permission check, or one embedding userinfo (user:pass@host). This fails closed because an absolute path to another host would retarget the proxied request past the permission check.","triggerScenarios":"HTTP(S)_PROXY environment variable or proxy agent set, plus http.request({ host: 'a.com', path: 'http://other.com/x' }) or path: 'http://user:pass@a.com/x'. The absolute path's host must equal the validated host and carry no credentials.","commonSituations":"Code that passes full URLs in options.path (which appears to work without a proxy); credential-bearing URLs moved into the path 'to keep them out of logs'; enabling a corporate proxy in CI and suddenly hitting this on existing request builders.","solutions":["Keep the target authority in options.host and the path origin-form: '/api/x?y=1'","Move credentials out of the path into options.auth or the Authorization header","If no proxy was intended, unset HTTP_PROXY/HTTPS_PROXY (and no_proxy as needed) for that request"],"exampleFix":"// before (with HTTP_PROXY set)\nhttp.request({ host: 'api.example.com', path: 'http://api.example.com/v1/x' }, cb);\n// OK only if identical authority; throws when the embedded host differs or has userinfo\n\n// after\nhttp.request({ host: 'api.example.com', path: '/v1/x' }, cb);","handlingStrategy":"validation","validationCode":"function assertProxySafePath(path, host) {\n  let u;\n  try { u = new URL(path); } catch { return; } // origin-form is fine\n  if (u.host !== host || u.username || u.password) {\n    throw new Error(`path '${path}' retargets the proxied request away from ${host}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try { http.request({ host, path }, cb); }\ncatch (err) {\n  if (err.code === 'ERR_INVALID_URL' && process.env.HTTP_PROXY) {\n    // strip scheme/userinfo from path, keep only pathname+search\n    const u = new URL(path);\n    return http.request({ host, path: u.pathname + u.search }, cb);\n  }\n  throw err;\n}","preventionTips":["Always pass the target authority in options.host and keep options.path origin-form","Put credentials in options.auth or the Authorization header, never inside the path","Make proxy usage explicit in tests so absolute-path builders fail there first"],"tags":["http","proxy","url","permissions","node-compat","deno"],"backgroundTag":"invalid-url","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"}