{"record":{"id":"51c6d8de90ab7f6c","repo":"musistudio/claude-code-router","slug":"base-url-is-required","errorCode":null,"errorMessage":"Base URL is required.","messagePattern":"Base URL is required\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/contracts/deep-link.ts","lineNumber":135,"sourceCode":"    throw new Error(\"Unsupported CCR link target.\");\n  }\n\n  const params = url.searchParams;\n  const payload = readPayloadRecord(params);\n  const name = boundedString(\n    firstStringParam(params, [\"name\"]) ??\n      firstPayloadString(payload, [\"name\"]),\n    maxNameLength,\n    \"Provider name\"\n  );\n  const baseUrl = boundedString(\n    firstStringParam(params, [\"base_url\"]) ??\n      firstPayloadString(payload, [\"base_url\"]),\n    maxBaseUrlLength,\n    \"Base URL\"\n  );\n  if (!baseUrl) {\n    throw new Error(\"Base URL is required.\");\n  }\n  validateProviderBaseUrl(baseUrl);\n\n  const apiKey = boundedString(\n    firstStringParam(params, [\"api_key\"]) ??\n      firstPayloadString(payload, [\"api_key\"]),\n    maxApiKeyLength,\n    \"API key\"\n  );\n  const icon = boundedString(\n    firstStringParam(params, [\"icon\"]) ??\n      firstPayloadString(payload, [\"icon\"]),\n    maxIconLength,\n    \"Provider icon\"\n  );\n  const protocol = normalizeProviderProtocol(\n    firstStringParam(params, [\"protocol\"]) ?? firstPayloadString(payload, [\"protocol\"])\n  );","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/contracts/deep-link.ts#L117-L153","documentation":"parseProviderDeepLinkPayload requires a base URL: after reading base_url from the query params or the JSON/base64 payload record (bounded by maxBaseUrlLength), an empty/missing value throws. The base URL is the minimum required field to create a provider entry.","triggerScenarios":"Calling parseProviderDeepLinkPayload with a ccr://provider link that has name= but no base_url= (neither in query nor payload), or base_url that is empty string / whitespace-trimmed to nothing.","commonSituations":"Hand-built links omitting base_url; payload JSON using a wrong key (baseUrl vs base_url); the field dropped by URL encoding bugs; user edited the link and removed the param.","solutions":["Add base_url=<provider endpoint> to the link's query params or payload record","Confirm the payload JSON uses snake_case base_url exactly","Pre-validate with URLSearchParams(url).get('base_url') before calling the parser"],"exampleFix":"// before\nconst link = \"ccr://provider/install?name=acme\";\n// after\nconst link = \"ccr://provider/install?name=acme&base_url=https%3A%2F%2Fapi.acme.dev%2Fv1\";","handlingStrategy":"validation","validationCode":"const u = new URL(rawUrl.trim()); if (!u.searchParams.get(\"base_url\")) return promptForBaseUrl();","typeGuard":"const hasBaseUrlParam = (u: string) => {\n  const p = new URL(u.trim()).searchParams;\n  return Boolean(p.get(\"base_url\") ?? JSON.parse(p.get(\"payload\") ?? \"{}\").base_url);\n};","tryCatchPattern":"try { parseProviderDeepLinkPayload(url); } catch (e) { if (e instanceof Error && e.message === \"Base URL is required.\") return collectBaseUrlThen(url); throw e; }","preventionTips":["Always include base_url when composing provider links","Use the exact snake_case key in payload JSON"],"tags":["deep-link","required-field","base-url"],"backgroundTag":"missing-required-parameter","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}