{"record":{"id":"1647cd80a397e755","repo":"denoland/deno","slug":"invalid-url-href-with-base-maybebase","errorCode":null,"errorMessage":"Invalid URL: '${href}' with base '${maybeBase}'","messagePattern":"Invalid URL: '(.+?)' with base '(.+?)'","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/web/00_url.js","lineNumber":113,"sourceCode":"    href,\n    maybeBase,\n    componentsBuf,\n  );\n}\n\n/**\n * @param {number} status\n * @param {string} href\n * @param {string} [maybeBase]\n * @returns {string}\n */\nfunction getSerialization(status, href, maybeBase) {\n  if (status === 0) {\n    return href;\n  } else if (status === 1) {\n    return op_url_get_serialization();\n  } else {\n    throw new TypeError(\n      `Invalid URL: '${href}'` +\n        (maybeBase ? ` with base '${maybeBase}'` : \"\"),\n    );\n  }\n}\n\nclass URLSearchParams {\n  [_list];\n  [_urlObject] = null;\n\n  /**\n   * @param {string | [string][] | Record<string, string>} init\n   */\n  constructor(init = undefined) {\n    this[webidl.brand] = webidl.brand;\n    // `undefined` is the default value of an optional argument, so it means\n    // \"not passed\". `null` is a value, and per WebIDL union resolution it\n    // reaches the USVString overload as \"null\".","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/00_url.js#L95-L131","documentation":"Deno's URL implementation (ext/web/00_url.js:113) parses via a Rust op; a non-zero failure status surfaces as TypeError carrying the offending href and base. This matches WHATWG URL parser rejections: input with no valid scheme when no base is supplied, an invalid base, or malformed components such as spaces in the host or unsupported scheme characters.","triggerScenarios":"new URL('not a url'); new URL('/relative/path') with no base; new URL('/path', 'notaurl'); an input host containing a space like 'http://exa mple.com'; a base whose scheme cannot be used for relative resolution.","commonSituations":"Building URLs from user input (search boxes, CLI arguments, query params) without validation; forgetting the base when resolving scraped relative links; empty or partially-populated env-provided endpoints; unescaped Unicode or control characters in hostnames.","solutions":["Wrap construction in try/catch and fall back to a sanitized default or a typed error of your own","Validate before parsing: check scheme with a regex or require a base","Encode user-controlled path/query pieces with encodeURIComponent before composing the URL string"],"exampleFix":"// before\nconst u = new URL(userInput);\n\n// after\nlet u: URL;\ntry {\n  u = new URL(userInput, 'https://example.com');\n} catch {\n  throw new Error(`invalid redirect target: ${userInput}`);\n}","handlingStrategy":"try-catch","validationCode":"const URL_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;\nif (!URL_RE.test(input) && !base) {\n  throw new Error(`not a URL: ${input}`);\n}","typeGuard":"function isParseableUrl(input: string, base?: string): boolean {\n  try {\n    new URL(input, base);\n    return true;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"let u: URL;\ntry {\n  u = new URL(input, baseUrl);\n} catch (e) {\n  if (e instanceof TypeError) {\n    throw new Error(`invalid URL from user input: ${JSON.stringify(input)}`);\n  }\n  throw e;\n}","preventionTips":["Never feed raw user input straight into new URL(); validate or try/catch first","Always pass a known-good base when input may be relative","Encode dynamic path/query segments with encodeURIComponent before composing"],"tags":["url","parsing","validation","web"],"backgroundTag":"invalid-url-parse","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}