affaan-m/ECC · error · AuthenticationError
{msg}
Error message
{msg} What it means
ClaudeProvider.generate() translates upstream API exceptions into typed errors; a message containing '401' or 'authentication' is re-raised as AuthenticationError with ProviderType.CLAUDE. This means the Anthropic-compatible API rejected the request's credentials before any completion was produced. The original SDK exception is chained via `from e` for diagnosis.
Solutions
- Confirm ANTHROPIC_API_KEY is set and non-empty in the runtime environment (check length/prefix only, never log it).
- Regenerate the key in the Anthropic console if it was rotated or revoked, and redeploy the secret.
- Validate the key with a direct curl call to the messages endpoint to isolate library vs credential issues.
- Ensure the key format and auth header match the API version the SDK expects (x-api-key, not Bearer).
Example fix
// before
provider = ClaudeProvider() # relies on missing ANTHROPIC_API_KEY
// after
key = os.getenv("ANTHROPIC_API_KEY", "").strip()
if not key.startswith("sk-ant-"):
raise RuntimeError("ANTHROPIC_API_KEY missing or malformed")
provider = ClaudeProvider(api_key=key) Defensive patterns
Strategy: try-catch
Validate before calling
key = os.getenv("ANTHROPIC_API_KEY", "").strip()
if not key.startswith("sk-ant-"):
raise RuntimeError("ANTHROPIC_API_KEY missing or malformed before calling generate()") Try / catch
try:
out = provider.generate(prompt)
except AuthenticationError as e:
log.error("claude auth rejected: %s", e)
rotate_credentials_and_retry_once() Prevention
- Validate the key's presence and format at process startup, not on first request.
- Store the key in a secret manager and re-inject after rotations.
- Smoke-test credentials with a minimal API call in deploy pipelines.
When it happens
Trigger: Calling generate() when the Claude API returns 401 Unauthorized — invalid/missing ANTHROPIC_API_KEY, malformed x-api-key header, revoked or expired key, or key belonging to a different organization than the requested workspace.
Common situations: ANTHROPIC_API_KEY unset in the deployment environment; key rotated and the old one still cached; copy-paste dropped characters from the key; CI secrets not injected; using an OpenAI-format key against the Claude endpoint.
Related errors
- msg
- {msg}
- AuthenticationError(msg, provider=ProviderType.OPENAI) from…
- Claude session not found
- ContextLengthError(msg, provider=ProviderType.CLAUDE) from e
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/83bbbc4284aaec9c.
Report an issue: GitHub.
Appendix: source
Thrown at src/llm/providers/claude.py:128
return LLMOutput(
content="".join(text_parts),
tool_calls=tool_calls or None,
model=response.model,
usage={
"input_tokens": response.usage.input_tokens,
"output_tokens": response.usage.output_tokens,
"cache_creation_input_tokens": getattr(
response.usage, "cache_creation_input_tokens", 0
),
"cache_read_input_tokens": getattr(response.usage, "cache_read_input_tokens", 0),
},
stop_reason=response.stop_reason,
)
except Exception as e:
msg = str(e)
if "401" in msg or "authentication" in msg.lower():
raise AuthenticationError(msg, provider=ProviderType.CLAUDE) from e
if "429" in msg or "rate_limit" in msg.lower():
raise RateLimitError(msg, provider=ProviderType.CLAUDE) from e
if "context" in msg.lower() and "length" in msg.lower():
raise ContextLengthError(msg, provider=ProviderType.CLAUDE) from e
raise
def list_models(self) -> list[ModelInfo]:
return self._models.copy()
def validate_config(self) -> bool:
return bool(self.client.api_key)
def get_default_model(self) -> str:
return _DEFAULT_MODEL
View on GitHub (pinned to 8321021c54)