Bug
When a provider API key is set to an empty string or whitespace-only value, the SDK sends an invalid Authorization: Bearer header instead of raising MissingCredentialsError.
Root Cause
get_credentials() and has_credential() in credentials.py only check if credential is None. Pydantic BaseSettings converts empty string env vars ("") to SecretStr(''), which passes the is not None check.
How to Reproduce
Any of these will trigger the bug:
# Empty .env value
HF_TOKEN=
# Empty shell export
export OPENAI_API_KEY=""
# Whitespace-only
export ANTHROPIC_API_KEY=" "
from celeste import create_client, Modality
# Instead of MissingCredentialsError, this sends "Authorization: Bearer "
# and crashes with: httpx.LocalProtocolError: Illegal header value b'Bearer '
client = create_client(modality=Modality.TEXT, model="gpt-4")
await client.generate(prompt="Hi")
Affected Code
src/celeste/credentials.py — get_credentials() (line ~118) and has_credential() (line ~145) only check is not None
src/celeste/auth.py — AuthHeader.convert_to_secret() strips whitespace but doesn't reject empty result
Impact
- All providers using the
AuthHeader path are affected (OpenAI, Anthropic, Groq, HuggingFace, Mistral, etc.)
- Ollama is NOT affected — it uses
NoAuth which bypasses AuthHeader entirely
- GitHub Actions CI is a common trigger:
${{ secrets.MISSING_SECRET }} resolves to "" when the secret doesn't exist
- User experience degrades: cryptic
httpx.LocalProtocolError instead of clear MissingCredentialsError
Proposed Fix
get_credentials() — reject empty/whitespace secrets with MissingCredentialsError
has_credential() — treat empty/whitespace as "not configured"
- Update
test_whitespace_credentials in tests/unit_tests/test_credentials.py to expect the new behavior
Bug
When a provider API key is set to an empty string or whitespace-only value, the SDK sends an invalid
Authorization: Bearerheader instead of raisingMissingCredentialsError.Root Cause
get_credentials()andhas_credential()incredentials.pyonly checkif credential is None. Pydantic BaseSettings converts empty string env vars ("") toSecretStr(''), which passes theis not Nonecheck.How to Reproduce
Any of these will trigger the bug:
Affected Code
src/celeste/credentials.py—get_credentials()(line ~118) andhas_credential()(line ~145) only checkis not Nonesrc/celeste/auth.py—AuthHeader.convert_to_secret()strips whitespace but doesn't reject empty resultImpact
AuthHeaderpath are affected (OpenAI, Anthropic, Groq, HuggingFace, Mistral, etc.)NoAuthwhich bypassesAuthHeaderentirely${{ secrets.MISSING_SECRET }}resolves to""when the secret doesn't existhttpx.LocalProtocolErrorinstead of clearMissingCredentialsErrorProposed Fix
get_credentials()— reject empty/whitespace secrets withMissingCredentialsErrorhas_credential()— treat empty/whitespace as "not configured"test_whitespace_credentialsintests/unit_tests/test_credentials.pyto expect the new behavior