{"record":{"id":"a13264c2530ba204","repo":"upstash/context7","slug":"context7-base-url-must-not-contain-credentials","errorCode":null,"errorMessage":"Context7 base URL must not contain credentials","messagePattern":"Context7 base URL must not contain credentials","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/setup/deployment.ts","lineNumber":25,"sourceCode":"  | { kind: \"hosted\"; baseUrl: typeof DEFAULT_CONTEXT7_BASE_URL }\n  | { kind: \"custom\"; baseUrl: string };\nexport type CustomSetupDeployment = Extract<SetupDeployment, { kind: \"custom\" }>;\n\nexport function normalizeDeploymentBaseUrl(input?: string): string {\n  const raw = input?.trim() || DEFAULT_CONTEXT7_BASE_URL;\n  let url: URL;\n\n  try {\n    url = new URL(raw);\n  } catch {\n    throw new Error(`Invalid Context7 base URL: ${raw}`);\n  }\n\n  if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n    throw new Error(\"Context7 base URL must use http:// or https://\");\n  }\n  if (url.username || url.password) {\n    throw new Error(\"Context7 base URL must not contain credentials\");\n  }\n  if (url.search || url.hash) {\n    throw new Error(\"Context7 base URL must not contain a query string or fragment\");\n  }\n\n  url.pathname = url.pathname.replace(/\\/+$/, \"\") || \"/\";\n  const normalized = url.toString().replace(/\\/$/, \"\");\n  if (url.pathname.endsWith(\"/mcp\") || url.pathname.endsWith(\"/api\")) {\n    throw new Error(\"Pass the Context7 deployment root, without /mcp or /api\");\n  }\n  return normalized;\n}\n\nexport function resolveSetupDeployment(input?: string): SetupDeployment {\n  const baseUrl = normalizeDeploymentBaseUrl(input);\n  return baseUrl === DEFAULT_CONTEXT7_BASE_URL\n    ? { kind: \"hosted\", baseUrl: DEFAULT_CONTEXT7_BASE_URL }\n    : { kind: \"custom\", baseUrl };","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/upstash/context7/blob/4416fb855b8f752be735e34f943b5d0762701aad/packages/cli/src/setup/deployment.ts#L7-L43","documentation":"Thrown by normalizeDeploymentBaseUrl when the URL embeds userinfo credentials (username or password, e.g. https://user:pass@host). The CLI does not accept credentials in the base URL; authentication is handled separately via the auth flow.","triggerScenarios":"normalizeDeploymentBaseUrl receives a URL where url.username or url.password is non-empty, e.g. 'https://admin:secret@my-onprem.internal'.","commonSituations":"Pasting a URL that includes basic-auth credentials copied from a browser, cURL command, or reverse-proxy config; embedding an API token in the URL out of habit.","solutions":["Strip the user:pass@ portion and pass only scheme + host (+ port/path root).","Configure credentials through the CLI's auth options instead of the URL.","If the deployment requires basic auth at the proxy layer, set it up in your HTTP client/proxy, not the base URL."],"exampleFix":"// before\nctx7 setup --url https://admin:secret@my-onprem.internal\n\n// after\nctx7 setup --url https://my-onprem.internal","handlingStrategy":"validation","validationCode":"function urlHasCredentials(raw: string): boolean {\n  try {\n    const u = new URL(raw);\n    return Boolean(u.username || u.password);\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const dep = resolveSetupDeployment(input);\n} catch (e) {\n  if ((e as Error).message.includes('credentials')) {\n    console.error('Strip user:pass@ from the URL and use the auth flow instead.');\n  }\n}","preventionTips":["Strip userinfo from URLs copied from browsers or cURL commands.","Never embed tokens or passwords in URLs; use headers/config.","Scrub secrets from pasted config before committing or sharing."],"tags":["url","security","credentials","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"4416fb855b8f752be735e34f943b5d0762701aad","analyzedAt":"2026-09-16T20:28:07.148Z","contentChangedAt":"2026-09-16T20:28:07.148Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}