fix: normalize Anthropic header keys to lowercase in provider registry - #1527
Conversation
The provider registry used PascalCase header keys (e.g. "Anthropic-Version")
while the Claude Code client path in base.ts sets lowercase keys
("anthropic-version"). Since JS object keys are case-sensitive, both keys
coexist in the headers object. When fetch() sends them, HTTP treats them
as duplicates and concatenates the values ("2023-06-01, 2023-06-01"),
causing Anthropic's API to reject the request with a 400 error.
Normalize all Anthropic-specific header keys to lowercase to match the
convention used in executors and the upstream API.
There was a problem hiding this comment.
Code Review
This pull request normalizes several HTTP header keys to lowercase across various provider configurations in providerRegistry.ts and updates the corresponding unit tests. This change affects headers such as anthropic-version, anthropic-beta, and x-app. Feedback suggests further lowercasing remaining headers like User-Agent and X-Stainless-* to maintain consistency and prevent potential duplicate header issues during fetch calls.
| "anthropic-dangerous-direct-browser-access": "true", | ||
| "User-Agent": CLAUDE_CLI_USER_AGENT, | ||
| "X-App": "cli", | ||
| "x-app": "cli", |
There was a problem hiding this comment.
The normalization of x-app to lowercase is consistent with the Anthropic headers, but the X-Stainless-* headers (lines 281-289) and the User-Agent header (line 279) remain in PascalCase. Since JavaScript object keys are case-sensitive, any other code path setting these headers in lowercase will cause fetch() to send duplicate headers (concatenated values), which is the root cause of the issue described in this PR. Consider lowercasing all header keys in this block for consistency, especially since other providers like github already use lowercase keys (e.g., user-agent).
diegosouzapw#1527) The provider registry used PascalCase header keys (e.g. "Anthropic-Version") while the Claude Code client path in base.ts sets lowercase keys ("anthropic-version"). Since JS object keys are case-sensitive, both keys coexist in the headers object. When fetch() sends them, HTTP treats them as duplicates and concatenates the values ("2023-06-01, 2023-06-01"), causing Anthropic's API to reject the request with a 400 error. Normalize all Anthropic-specific header keys to lowercase to match the convention used in executors and the upstream API.
diegosouzapw#1527) The provider registry used PascalCase header keys (e.g. "Anthropic-Version") while the Claude Code client path in base.ts sets lowercase keys ("anthropic-version"). Since JS object keys are case-sensitive, both keys coexist in the headers object. When fetch() sends them, HTTP treats them as duplicates and concatenates the values ("2023-06-01, 2023-06-01"), causing Anthropic's API to reject the request with a 400 error. Normalize all Anthropic-specific header keys to lowercase to match the convention used in executors and the upstream API.
Summary
providerRegistry.tsfrom PascalCase ("Anthropic-Version","Anthropic-Beta", etc.) to lowercase ("anthropic-version","anthropic-beta", etc.)Problem
The provider registry uses PascalCase header keys (e.g.
"Anthropic-Version") while the Claude Code client path inbase.ts(added in v3.6.9) uses lowercase keys ("anthropic-version"). Since JavaScript object keys are case-sensitive, both keys coexist in the headers object. Whenfetch()sends them, HTTP treats them as duplicates and concatenates the values:This causes Anthropic's API to reject the request with a 400 error:
{"error":{"message":"[400]: anthropic-version: \"2023-06-01, 2023-06-01\" is not a valid version","type":"invalid_request_error","code":"bad_request"}}Root cause
Two independent code paths setting the same HTTP header with different JS key casing:
providerRegistry.ts(v1.0.0) —"Anthropic-Version": ANTHROPIC_VERSION_HEADER(PascalCase)base.tsccHeaders (v3.6.9) —"anthropic-version": "2023-06-01"(lowercase)Test plan
t20-t22-provider-headers.test.ts— passes with lowercase assertionssearch-provider-validation.test.ts— passes with lowercase assertion