musistudio/claude-code-router · error · Error

${safetyIssue.message}

Error message

${safetyIssue.message}

What it means

resolveGatewayProviderProbe runs providerApiKeySafetyIssue on the request's apiKey + baseUrl before probing and rethrows its message. This blocks probes that would send an API key to a host where it does not belong (key/host preset mismatch), preventing accidental credential leakage during probing.

Source

Thrown at packages/core/src/providers/probe.ts:344

    startedAt: check.startedAt,
    statusCode: protocol?.status ?? (successful ? 200 : 599),
    url: protocol?.endpoint ?? candidate?.baseUrl ?? "provider-connectivity-check"
  });
}

function providerProbeCandidateName(candidate: GatewayProviderProbeCandidate | undefined): string | undefined {
  const value: unknown = candidate;
  return isRecord(value) ? readString(value.name) : undefined;
}

async function resolveGatewayProviderProbe(request: GatewayProviderProbeRequest): Promise<GatewayProviderProbeResult> {
  const mode = request.mode ?? "protocols";
  const safetyIssue = providerApiKeySafetyIssue({
    apiKey: request.apiKey,
    baseUrl: request.baseUrl
  });
  if (safetyIssue) {
    throw new Error(safetyIssue.message);
  }

  const parsed = parseProviderUrl(request.baseUrl);
  const protocols = providerProbeProtocolsForBaseUrl(request.baseUrl, request.protocols ?? []);
  const typedModels = uniqueStrings(request.models ?? []);
  const modelProbe = mode !== "models" || request.skipModelDiscovery
    ? { models: [] }
    : await probeModels(parsed, request.apiKey, protocols, request.providerPlugins ?? []);
  const models = (mode === "connectivity" || mode === "models") && modelProbe.models.length > 0
    ? modelProbe.models
    : typedModels;
  const protocolResults = await probeProtocols(parsed, request.apiKey, models, protocols, mode, request.providerPlugins ?? []);
  const detectedProtocol = detectProtocol(parsed, protocolResults, modelProbe.source, protocols);
  const normalizedBaseUrl = detectedProtocol
    ? resolveProbeBaseUrl(parsed, detectedProtocol, protocolResults, modelProbe)
    : parsed.normalizedInputBaseUrl;
  const detectedProvider = detectProvider(protocolResults);
  const account = detectedProvider === "new-api" ? newApiKeyUsageAccountConfig(normalizedBaseUrl) : undefined;

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Match the API key vendor with the base URL preset (e.g. Anthropic key → Anthropic preset URL)
  2. If using a gateway/proxy, configure it under a custom provider preset whose base URL matches, rather than overriding a bundled preset's URL
  3. Double-check for typos in baseUrl (trailing paths, wrong subdomain)
  4. Rotate the key if it was previously sent to the wrong host

Example fix

// before
probe({ apiKey: anthropicKey, baseUrl: "https://api.openai.com/v1", protocols: [...] })
// after
probe({ apiKey: openaiKey, baseUrl: "https://api.openai.com/v1", protocols: [...] })
Defensive patterns

Strategy: try-catch

Validate before calling

const issue = providerApiKeySafetyIssue({ apiKey: request.apiKey, baseUrl: request.baseUrl });
if (issue) throw new Error(issue.message);

Try / catch

try { await resolveGatewayProviderProbe(request); } catch (e) { if (e instanceof Error && /api key/i.test(e.message)) return { ok: false, reason: 'key-host-mismatch' }; throw e; }

Prevention

When it happens

Trigger: probe() with a baseUrl that does not match the provider preset the apiKey was issued for, or a baseUrl/host combination flagged by providerApiKeySafetyIssue (wrong vendor endpoint, impersonated host).

Common situations: Using an Anthropic key against an OpenAI-format base URL, pointing a vendor key at a proxy domain not in the preset's allowlist, or copy-pasting base URLs between provider configs.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/cb48fbd6011913d5. Report an issue: GitHub.