denoland/deno · error · TypeError
ERR_INVALID_URL
ERR_INVALID_URL
Error message
Invalid URL: ${path} What it means
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.
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
Example fix
// before (with HTTP_PROXY set)
http.request({ host: 'api.example.com', path: 'http://api.example.com/v1/x' }, cb);
// OK only if identical authority; throws when the embedded host differs or has userinfo
// after
http.request({ host: 'api.example.com', path: '/v1/x' }, cb); Defensive patterns
Strategy: validation
Validate before calling
function assertProxySafePath(path, host) {
let u;
try { u = new URL(path); } catch { return; } // origin-form is fine
if (u.host !== host || u.username || u.password) {
throw new Error(`path '${path}' retargets the proxied request away from ${host}`);
}
} Try / catch
try { http.request({ host, path }, cb); }
catch (err) {
if (err.code === 'ERR_INVALID_URL' && process.env.HTTP_PROXY) {
// strip scheme/userinfo from path, keep only pathname+search
const u = new URL(path);
return http.request({ host, path: u.pathname + u.search }, cb);
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/9cca0b286898ab5d.
Report an issue: GitHub.
Appendix: source
Thrown at ext/node/polyfills/_http_client.js:492
// An absolute-form path is already a complete target. It is only honored
// when its authority matches the one that `--allow-net` validated and it
// carries no userinfo: the permission check upstream sees only `host` and
// `port`, so an absolute `path` aimed at another authority (or embedding
// credentials for one) would otherwise retarget the proxied request past
// that check. Fail closed instead - like a denied direct connection.
let absolute = null;
try {
absolute = new URL(path);
} catch {
// Not absolute-form; resolved as an origin-form path below.
}
if (absolute !== null) {
if (
absolute.host !== base.host ||
absolute.username !== "" ||
absolute.password !== ""
) {
throw new ERR_INVALID_URL(path);
}
return absolute.href;
}
try {
return new URL(base.origin + path).href;
} catch {
const portSuffix = +port === 80 ? "" : `:${port}`;
return `http://${authorityHost}${portSuffix}${path}`;
}
}
function ClientRequest(input, options, cb) {
FunctionPrototypeCall(OutgoingMessage, this);
if (typeof input === "string") {
const urlStr = input;
// Match Node: `new URL(...)` in ClientRequest surfaces asView on GitHub (pinned to 9ad36f7a2c)