denoland/deno · critical · TypeError

Invalid URL

Error message

Invalid URL: ${raw}

What it means

In the node:http proxy support, parseProxyUrl validates values read from proxy environment variables (http_proxy/HTTP_PROXY, https_proxy/HTTPS_PROXY). In 'env' mode - active when NODE_USE_ENV_PROXY=1 or --use-env-proxy - a present-but-non-string value throws TypeError('Invalid URL: <value>'), mirroring whatwg URL constructor semantics that Node v24 surfaces at request time. The error propagates through resolveAgentProxyConfig and lands on the first request that needs the proxy.

Solutions

  1. Ensure proxy env vars are strings: set them as String(value) or via a proper shell export.
  2. Unset the malformed variable (or set it to empty string, which is treated as 'no proxy') so requests go direct.
  3. If you inject env programmatically, validate http_proxy/https_proxy/no_proxy with typeof === 'string' before startup.

Example fix

// before
Deno.env.set('HTTP_PROXY', 123 as any); // non-string
// NODE_USE_ENV_PROXY=1 -> first request throws TypeError

// after
Deno.env.set('HTTP_PROXY', 'http://proxy.local:8080');
Defensive patterns

Strategy: validation

Validate before calling

function assertProxyEnvStrings(env) {
  for (const k of ['http_proxy', 'HTTP_PROXY', 'https_proxy', 'HTTPS_PROXY']) {
    const v = env[k];
    if (v === undefined || v === '') continue;
    if (typeof v !== 'string') throw new Error(`${k} must be a string, got ${typeof v}`);
    new URL(v); // parseability pre-check
  }
}
assertProxyEnvStrings(Deno.env.toObject());

Try / catch

try {
  http.get('http://example.com/', cb);
} catch (err) {
  if (err instanceof TypeError && String(err.message).startsWith('Invalid URL')) {
    // proxy env var is malformed: fix/unset HTTP(S)_PROXY, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: NODE_USE_ENV_PROXY=1 with an env-like source (or buildProxyConfig(env, 'env')) where http_proxy is a non-string non-empty value, e.g. a number or boolean injected programmatically; then any http request triggers maybeInitFromEnv and throws.

Common situations: Programmatically seeded process.env/privileged env with non-string values; test harnesses passing objects where strings belong; tooling that copies env JSON verbatim into the runtime; virtually impossible via a real shell, which only produces string env values.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/4ef9362f5abc5d91. Report an issue: GitHub.

Appendix: source

Thrown at ext/node/polyfills/_http_proxy.js:108

}

function readEnvKey(env, keys) {
  for (let i = 0; i < keys.length; i++) {
    const k = keys[i];
    if (env[k] !== undefined && env[k] !== "") {
      return env[k];
    }
  }
  return undefined;
}

const CRLF_RE = new SafeRegExp(/[\r\n]/);

function parseProxyUrl(raw, kind, mode) {
  if (raw === undefined || raw === null || raw === "") return null;
  if (typeof raw !== "string") {
    if (mode === "env") {
      throw new TypeError(`Invalid URL: ${raw}`);
    }
    throw new ERR_PROXY_INVALID_CONFIG(
      `Invalid proxy URL for ${kind}: must be a string`,
    );
  }
  // CRLF injection guard - check raw string before URL parsing strips them.
  // Matches Node's CRLF rejection in the proxy URL validator. We surface this
  // even for env-derived URLs so the auth tests get the expected error class.
  if (RegExpPrototypeExec(CRLF_RE, raw) !== null) {
    throw new ERR_PROXY_INVALID_CONFIG(`Invalid proxy URL: ${raw}`);
  }
  let url;
  try {
    url = new URL(raw);
  } catch {
    if (mode === "env") {
      throw new TypeError(`Invalid URL: ${raw}`);
    }

View on GitHub (pinned to 9ad36f7a2c)