{"record":{"id":"9cca0b286898ab5d","repo":"denoland/deno","slug":"err-invalid-url","errorCode":"ERR_INVALID_URL","errorMessage":"Invalid URL: ${urlStr}","messagePattern":"Invalid URL: (.+?)","errorType":"validation","errorClass":"NodeTypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_http_client.js","lineNumber":464,"sourceCode":"\nfunction isURL(input) {\n  return ObjectPrototypeIsPrototypeOf(URL.prototype, input);\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\n    // ERR_INVALID_URL (node's internal URL constructor calls\n    // bindingUrl.parse with raiseException=true). Deno's Web URL\n    // throws a generic TypeError, so wrap it to attach the code.\n    let parsed;\n    try {\n      parsed = new URL(urlStr);\n    } catch {\n      throw new ERR_INVALID_URL(urlStr);\n    }\n    input = urlToHttpOptions(parsed);\n  } else if (isURL(input)) {\n    input = urlToHttpOptions(input);\n  } else {\n    cb = options;\n    options = input;\n    input = null;\n  }\n\n  if (typeof options === \"function\") {\n    cb = options;\n    options = input || kEmptyObject;\n  } else {\n    options = ObjectAssign(input || {}, options);\n  }\n\n  let agent = options.agent;","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_http_client.js#L446-L482","documentation":"When ClientRequest receives a string as its first argument, the polyfill parses it with new URL(urlStr). Deno's Web URL throws a generic TypeError on bad input, so the polyfill wraps it to attach Node's ERR_INVALID_URL code and the 'Invalid URL: <input>' message. It fires only for the string-input form; URL objects and options objects go through other branches.","triggerScenarios":"http.get('not-a-url'); http.request('example.com/path') (missing protocol); malformed bracket syntax like 'http://[::1:80/x'; strings built by concatenation that drop the scheme or contain spaces.","commonSituations":"Forgetting the http:// prefix when the value comes from an env var or config that stores a bare host; user-supplied URLs pasted into a CLI; template literals that interpolate undefined into the URL string.","solutions":["Include the scheme: 'http://' + host + path, or better, construct with new URL(path, base) first","Validate before calling: if (!URL.canParse(urlStr)) fail with your own message","Log the offending string when catching to find where the malformed value originates"],"exampleFix":"// before\nconst req = http.request(`${cfg.host}/api`); // cfg.host = 'api.example.com'\n\n// after\nconst target = new URL('/api', `http://${cfg.host}`);\nconst req = http.request(target);","handlingStrategy":"validation","validationCode":"// Node >= 18.17 / Deno\nif (!URL.canParse(urlStr)) {\n  throw new Error(`invalid target URL: ${JSON.stringify(urlStr)}`);\n}\nhttp.request(urlStr);","typeGuard":"function isParseableUrl(v) {\n  if (typeof v !== 'string' || !v) return false;\n  try { new URL(v); return true; } catch { return false; }\n}","tryCatchPattern":"try { http.get(urlStr, cb); } catch (e) { if (e.code === 'ERR_INVALID_URL') { /* log e.input, reject user input */ } else throw e; }","preventionTips":["Always construct targets via new URL(path, base)","Reject URLs missing a scheme at config-load time","Never interpolate undefined/null into URL template strings"],"tags":["http","url","client","validation","node-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}