fix(proxy): remove x-api-key when OAuth Authorization header is present

When ANTHROPIC_AUTH_TOKEN is set and forward_client_headers is true,
Claude Code sends x-litellm-api-key: Bearer <oauth_token>. The proxy
forwards this as x-api-key (via ANTHROPIC_API_HEADERS) AND adds
Authorization: Bearer <oauth_token> (via OAuth detection).

Both headers end up in provider_specific_headers.extra_headers. However,
when the Anthropic API receives both x-api-key and Authorization with
the same OAuth token value, it uses x-api-key, which fails for OAuth
tokens (x-api-key requires sk-ant-api* format, not sk-ant-oat*).

Fix: when we detect an OAuth Authorization header, remove x-api-key
from the provider-specific headers since it would take precedence at
the HTTP level and cause 'Invalid key=value pair' errors.

Fixes #24436
This commit is contained in:
BillionClaw 2026-03-25 08:24:34 +08:00
parent b58c1538a4
commit 29f728c089
2 changed files with 39 additions and 0 deletions

View file

@ -1945,6 +1945,11 @@ def add_provider_specific_headers_to_request(
if header.lower() == "authorization" and is_anthropic_oauth_key(value):
anthropic_headers[header] = value
added_header = True
# Remove x-api-key from headers since it's incompatible with OAuth tokens.
# When both Authorization and x-api-key are sent with the same token value,
# the Anthropic API uses x-api-key (which fails for OAuth format).
# We must keep only Authorization for OAuth tokens.
anthropic_headers.pop("x-api-key", None)
break
if added_header is True:
# Anthropic headers work across multiple providers

View file

@ -511,6 +511,40 @@ class TestProxyOAuthHeaderForwarding:
assert psh["extra_headers"]["authorization"] == f"Bearer {FAKE_OAUTH_TOKEN}"
assert psh["extra_headers"]["anthropic-beta"] == "oauth-2025-04-20"
def test_add_provider_specific_headers_oauth_removes_conflicting_x_api_key(self):
"""When both x-api-key and OAuth Authorization are present, x-api-key must be
removed to prevent the Anthropic API from preferring it over Authorization.
Regression test for https://github.com/BerriAI/litellm/issues/24436
When ANTHROPIC_AUTH_TOKEN is set and forward_client_headers is true, Claude Code
sends x-litellm-api-key: Bearer <oauth_token>. The proxy forwards this as
x-api-key (from ANTHROPIC_API_HEADERS). But when we also add
Authorization: Bearer <oauth_token>, the Anthropic API uses x-api-key (which
fails for OAuth tokens since x-api-key requires sk-ant-api* format).
Fix: remove x-api-key when we detect an OAuth Authorization header.
"""
from litellm.proxy.litellm_pre_call_utils import (
add_provider_specific_headers_to_request,
)
data: dict = {}
# This is what the proxy receives from Claude Code when ANTHROPIC_AUTH_TOKEN is set:
# x-api-key from ANTHROPIC_API_HEADERS + authorization from OAuth detection
headers = {
"x-api-key": f"Bearer {FAKE_OAUTH_TOKEN}",
"authorization": f"Bearer {FAKE_OAUTH_TOKEN}",
"content-type": "application/json",
}
add_provider_specific_headers_to_request(data=data, headers=headers)
assert "provider_specific_header" in data
psh = data["provider_specific_header"]
assert psh["extra_headers"]["authorization"] == f"Bearer {FAKE_OAUTH_TOKEN}"
# x-api-key must NOT be present - it would take precedence over Authorization
# at the HTTP level, causing "Invalid key=value pair" errors for OAuth tokens
assert "x-api-key" not in psh["extra_headers"]
def test_clean_headers_forwards_x_api_key_when_authenticated_with_litellm_key(self):
"""clean_headers should forward x-api-key when user authenticated with x-litellm-api-key and forward_llm_provider_auth_headers=True."""
from starlette.datastructures import Headers