Skip to content

fix(client): treat empty-string env credentials as unset#1641

Open
LiFeiYu-boost wants to merge 1 commit into
anthropics:mainfrom
LiFeiYu-boost:fix/empty-string-env-credentials
Open

fix(client): treat empty-string env credentials as unset#1641
LiFeiYu-boost wants to merge 1 commit into
anthropics:mainfrom
LiFeiYu-boost:fix/empty-string-env-credentials

Conversation

@LiFeiYu-boost
Copy link
Copy Markdown

Summary

Fixes #1640.

When ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN are present in the environment but set to an empty string (common in CI, containers, and shells that export VAR= — including the Claude Code CLI shell), the client treated "" as a real credential because every guard checks is None. It then built a malformed Authorization: Bearer header (trailing space), which h11 rejects at request-write time — so every request failed with APIConnectionError.

Change

Coerce empty-string env credentials to None at the read site, in both Anthropic.__init__ and AsyncAnthropic.__init__:

api_key = os.environ.get("ANTHROPIC_API_KEY") or None
auth_token = os.environ.get("ANTHROPIC_AUTH_TOKEN") or None

Empty env credentials now fall through to the normal no-credential path: default_credentials() auto-discovery (profile / federation), or a clear Could not resolve authentication method error — instead of emitting a malformed header.

This also makes _client.py consistent with the existing credential chain: lib/credentials/_chain.py already treats empty env vars as absent via truthy checks (if os.environ.get(ENV_API_KEY):), and uses the same or None idiom for ANTHROPIC_WORKSPACE_ID.

Explicitly-passed api_key="" / auth_token="" constructor arguments are intentionally left unchanged — this PR only normalizes the implicit env-var read.

Tests

Added test_empty_env_credentials_treated_as_unset to both TestAnthropic and TestAsyncAnthropic, mirroring the existing test_validate_headers pattern (with default_credentials mocked out): asserts api_key / auth_token resolve to None, auth_headers is empty (previously {"X-Api-Key": "", "Authorization": "Bearer "}), and request building raises the standard auth-resolution TypeError.

tests/test_client.py passes; ruff check, ruff format --check, and pyright are clean on the touched files.

When ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN are present in the
environment but set to an empty string (common in CI, containers, and
shells that `export VAR=`), the client treated "" as a real credential
because every guard checks `is None`. This produced a malformed
`Authorization: Bearer ` header (trailing space) that h11 rejects at
request-write time, so every request failed with APIConnectionError.

Coerce empty-string env credentials to None at the read site so they
fall through to the normal no-credential path (default_credentials
auto-discovery, or a clear "Could not resolve authentication method"
error). This also matches the credential chain in
lib/credentials/_chain.py, which already treats empty env vars as
absent via truthy checks.

Fixes anthropics#1640
@LiFeiYu-boost LiFeiYu-boost requested a review from a team as a code owner June 4, 2026 01:38
@hiroki-tamba-research
Copy link
Copy Markdown

Thanks for the fast fix — this resolves the reported (env) case cleanly, and coercing at the read site is the robust choice: it covers every downstream guard (the missing-auth check and both _api_key_auth / _bearer_auth) in one place, for both sync and async. Tests over ANTHROPIC_API_KEY + ANTHROPIC_AUTH_TOKEN are 👍.

One optional, non-blocking note for defense-in-depth: an explicitly-passed empty string still slips through, since it skips the env-read coercion (has_explicit_credential is is not None) and _bearer_auth guards on is None:

>>> from anthropic import Anthropic
>>> dict(Anthropic(auth_token="").auth_headers)
{'Authorization': 'Bearer '}   # same malformed header

Making the auth_headers builders truthy (if not auth_token / if not api_key) would close that path too. Not needed for #1640 (the env case) — just flagging while we're here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Anthropic() emits a malformed Authorization: Bearer header when ANTHROPIC_AUTH_TOKEN is an empty string

2 participants