upstash/context7 · error

Context7 base URL must not contain credentials

Error message

Context7 base URL must not contain credentials

What it means

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.

Solutions

  1. Strip the user:pass@ portion and pass only scheme + host (+ port/path root).
  2. Configure credentials through the CLI's auth options instead of the URL.
  3. If the deployment requires basic auth at the proxy layer, set it up in your HTTP client/proxy, not the base URL.

Example fix

// before
ctx7 setup --url https://admin:secret@my-onprem.internal

// after
ctx7 setup --url https://my-onprem.internal
Defensive patterns

Strategy: validation

Validate before calling

function urlHasCredentials(raw: string): boolean {
  try {
    const u = new URL(raw);
    return Boolean(u.username || u.password);
  } catch { return false; }
}

Try / catch

try {
  const dep = resolveSetupDeployment(input);
} catch (e) {
  if ((e as Error).message.includes('credentials')) {
    console.error('Strip user:pass@ from the URL and use the auth flow instead.');
  }
}

Prevention

When it happens

Trigger: normalizeDeploymentBaseUrl receives a URL where url.username or url.password is non-empty, e.g. 'https://admin:secret@my-onprem.internal'.

Common situations: 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.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of upstash/context7@4416fb855b (2026-09-16). Data as JSON: /api/errors/a13264c2530ba204. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/setup/deployment.ts:25

  | { kind: "hosted"; baseUrl: typeof DEFAULT_CONTEXT7_BASE_URL }
  | { kind: "custom"; baseUrl: string };
export type CustomSetupDeployment = Extract<SetupDeployment, { kind: "custom" }>;

export function normalizeDeploymentBaseUrl(input?: string): string {
  const raw = input?.trim() || DEFAULT_CONTEXT7_BASE_URL;
  let url: URL;

  try {
    url = new URL(raw);
  } catch {
    throw new Error(`Invalid Context7 base URL: ${raw}`);
  }

  if (url.protocol !== "http:" && url.protocol !== "https:") {
    throw new Error("Context7 base URL must use http:// or https://");
  }
  if (url.username || url.password) {
    throw new Error("Context7 base URL must not contain credentials");
  }
  if (url.search || url.hash) {
    throw new Error("Context7 base URL must not contain a query string or fragment");
  }

  url.pathname = url.pathname.replace(/\/+$/, "") || "/";
  const normalized = url.toString().replace(/\/$/, "");
  if (url.pathname.endsWith("/mcp") || url.pathname.endsWith("/api")) {
    throw new Error("Pass the Context7 deployment root, without /mcp or /api");
  }
  return normalized;
}

export function resolveSetupDeployment(input?: string): SetupDeployment {
  const baseUrl = normalizeDeploymentBaseUrl(input);
  return baseUrl === DEFAULT_CONTEXT7_BASE_URL
    ? { kind: "hosted", baseUrl: DEFAULT_CONTEXT7_BASE_URL }
    : { kind: "custom", baseUrl };

View on GitHub (pinned to 4416fb855b)