earendil-works/pi · error
No API key for provider: ${provider}
Error message
No API key for provider: ${provider} What it means
Before building an Anthropic SDK client, the anthropic-messages stream function asserts authentication via assertRequestAuth: either options.apiKey is set, or the merged headers contain a non-empty authorization, x-api-key, or cf-aig-authorization value (case-insensitive keys). If none is present it throws 'No API key for provider: <provider>' (e.g. anthropic, github-copilot). Supplying options.client - such as an AnthropicVertex instance - skips the check entirely because auth lives inside that client.
Source
Thrown at packages/ai/src/api/anthropic-messages.ts:306
function hasHeader(headers: ProviderHeaders | undefined, name: string): boolean {
if (!headers) return false;
const expected = name.toLowerCase();
for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() === expected && value !== null && value.trim().length > 0) return true;
}
return false;
}
function assertRequestAuth(provider: string, apiKey: string | undefined, headers: ProviderHeaders | undefined): void {
if (apiKey) return;
if (
hasHeader(headers, "authorization") ||
hasHeader(headers, "x-api-key") ||
hasHeader(headers, "cf-aig-authorization")
) {
return;
}
throw new Error(`No API key for provider: ${provider}`);
}
interface ServerSentEvent {
event: string | null;
data: string;
raw: string[];
}
interface SseDecoderState {
event: string | null;
data: string[];
raw: string[];
}
const ANTHROPIC_MESSAGE_EVENTS: ReadonlySet<string> = new Set([
"message_start",
"message_delta",
"message_stop",View on GitHub (pinned to 4af9d21d3b)
Solutions
- Set the apiKey option when building stream options, typically read from the provider's env var at startup
- Or pass an auth header: authorization (Bearer ...), x-api-key, or cf-aig-authorization for Cloudflare AI Gateway
- Or inject options.client (e.g. AnthropicVertex) when auth is not key-based
- Fail fast at startup: assert the key resolves before the first request, not mid-conversation
Example fix
// before
const result = await streamSimple(model, context, {}); // no apiKey, no headers
// after
const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) throw new Error("ANTHROPIC_API_KEY is not set");
const result = await streamSimple(model, context, { apiKey }); Defensive patterns
Strategy: validation
Validate before calling
const hasAuth = Boolean(
apiKey ||
headers?.authorization ||
headers?.["x-api-key"] ||
headers?.["cf-aig-authorization"],
);
if (!hasAuth && !options?.client) {
throw new Error(`No API key for provider: ${model.provider}`);
}
await stream(model, context, { ...options, apiKey, headers }); Type guard
const canAuthenticate = (opts: { apiKey?: string; headers?: Record<string, string>; client?: unknown }): boolean =>
Boolean(opts.client || opts.apiKey || hasNonEmptyAuthHeader(opts.headers)); Prevention
- Resolve and assert provider keys once at startup, failing fast with the env var name
- Load .env files before building stream options
- For gateway or Vertex setups, pass cf-aig-authorization headers or options.client instead of a key
- Remember hasHeader requires a non-empty value - a blank string header does not count
When it happens
Trigger: Calling stream or streamSimple on an anthropic-messages model with no apiKey option, no auth headers, and no custom client; the host resolves the key from an env var that is unset in the current shell, container, or CI runner; the key lookup returns empty string.
Common situations: ANTHROPIC_API_KEY (or the host's own env mapping) missing in CI, Docker, or a deployed environment; .env loaded after stream options are built; gateway setups (Cloudflare AI Gateway, Vertex) that should pass cf-aig-authorization headers or options.client instead of an API key.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
- Proxy error: ${response.status} ${response.statusText}
- No default stream function configured. Pass streamFn explici
AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24).
Data as JSON: /api/errors/ec37dc688ae555b7.
Report an issue: GitHub.