{"record":{"id":"4be7d864d0ab09dc","repo":"denoland/deno","slug":"err-proxy-invalid-config","errorCode":"ERR_PROXY_INVALID_CONFIG","errorMessage":"Invalid proxy URL for ${kind}: must be a string","messagePattern":"Invalid proxy URL for (.+?): must be a string","errorType":"exception","errorClass":"NodeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_proxy.js","lineNumber":110,"sourceCode":"function readEnvKey(env, keys) {\n  for (let i = 0; i < keys.length; i++) {\n    const k = keys[i];\n    if (env[k] !== undefined && env[k] !== \"\") {\n      return env[k];\n    }\n  }\n  return undefined;\n}\n\nconst CRLF_RE = new SafeRegExp(/[\\r\\n]/);\n\nfunction parseProxyUrl(raw, kind, mode) {\n  if (raw === undefined || raw === null || raw === \"\") return null;\n  if (typeof raw !== \"string\") {\n    if (mode === \"env\") {\n      throw new TypeError(`Invalid URL: ${raw}`);\n    }\n    throw new ERR_PROXY_INVALID_CONFIG(\n      `Invalid proxy URL for ${kind}: must be a string`,\n    );\n  }\n  // CRLF injection guard - check raw string before URL parsing strips them.\n  // Matches Node's CRLF rejection in the proxy URL validator. We surface this\n  // even for env-derived URLs so the auth tests get the expected error class.\n  if (RegExpPrototypeExec(CRLF_RE, raw) !== null) {\n    throw new ERR_PROXY_INVALID_CONFIG(`Invalid proxy URL: ${raw}`);\n  }\n  let url;\n  try {\n    url = new URL(raw);\n  } catch {\n    if (mode === \"env\") {\n      throw new TypeError(`Invalid URL: ${raw}`);\n    }\n    throw new ERR_PROXY_INVALID_CONFIG(`Invalid proxy URL: ${raw}`);\n  }","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_http_proxy.js#L92-L128","documentation":"In strict mode (an explicit proxyEnv object passed to http.setGlobalProxyFromEnv(env) or new http.Agent({ proxyEnv })), parseProxyUrl throws ERR_PROXY_INVALID_CONFIG 'must be a string' when the value for http_proxy/https_proxy/HTTPS_PROXY-style keys is present but not a string (number, boolean, object).","triggerScenarios":"setGlobalProxyFromEnv({ http_proxy: 123 }) or new Agent({ proxyEnv: { https_proxy: true } }); note undefined/null/'' are treated as unset and return null instead of throwing.","commonSituations":"Proxy config parsed from JSON/YAML where the port or whole URL ended up as a number/boolean; config templating that interpolates values without quoting.","solutions":["Pass URL strings in proxyEnv objects: { https_proxy: 'http://proxy:8080' }","Validate your config object before handing it to the API (see validation below)","Omit the key entirely (or leave it undefined) when no proxy should be configured"],"exampleFix":"// before\nhttp.setGlobalProxyFromEnv({ http_proxy: config.port }); // number -> throws\n\n// after\nhttp.setGlobalProxyFromEnv({ http_proxy: `http://${config.host}:${config.port}` });","handlingStrategy":"type-guard","validationCode":"function normalizeProxyEnv(env: Record<string, unknown>) {\n  const out: Record<string, string> = {};\n  for (const [k, v] of Object.entries(env)) {\n    if (v === undefined || v === null || v === '') continue;\n    out[k] = String(v); // coerce numbers/booleans to strings\n  }\n  return out;\n}","typeGuard":"function isStringProxyEnv(env: unknown): env is Record<string, string> {\n  return !!env && typeof env === 'object' && !Array.isArray(env) &&\n    Object.values(env).every((v) => v === undefined || typeof v === 'string');\n}","tryCatchPattern":"try {\n  http.setGlobalProxyFromEnv(proxyEnv);\n} catch (e) {\n  if (e?.code === 'ERR_PROXY_INVALID_CONFIG' && /must be a string/.test(e.message)) {\n    http.setGlobalProxyFromEnv(normalizeProxyEnv(proxyEnv)); // retry coerced\n  } else throw e;\n}","preventionTips":["Quote proxy URLs in JSON/YAML config so they stay strings","Validate proxyEnv shape at config load time","Remember empty string/undefined/null keys mean 'unset', not error"],"tags":["proxy","config","types","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}