affaan-m/ECC · error · AuthenticationError
{msg}
Error message
{msg} What it means
ClaudeProvider.generate() wraps the Anthropic SDK; when an exception's message contains '429' or 'rate_limit' it is re-raised as RateLimitError tagged ProviderType.CLAUDE. Anthropic returns HTTP 429 with a rate_limit_error type, and the SDK message typically embeds the status code, so the substring match catches it. Claude also exposes cache_creation/cache_read token accounting (visible in the surrounding usage block), and prompt-caching misses are a common driver of input-token pressure that triggers the limit.
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 01e15490f0)
Solutions
- Wrap generate() with exponential backoff keyed on RateLimitError (respect any Retry-After the SDK exposes).
- Cap concurrency per Anthropic workspace/API key.
- Enable prompt caching (cache_creation_input_tokens) to cut billable input tokens.
- Move non-interactive workloads to the Anthropic batch API or upgrade the rate-limit tier.
Example fix
// before
output = provider.generate(llm_input)
// after
import time
from llm.core.interface import RateLimitError
for attempt in range(5):
try:
output = provider.generate(llm_input)
break
except RateLimitError:
if attempt == 4:
raise
time.sleep(2 ** attempt) Defensive patterns
Strategy: retry
Type guard
from llm.core.interface import RateLimitError
def is_rate_limit(exc: BaseException) -> bool:
return isinstance(exc, RateLimitError) Try / catch
import time
from llm.core.interface import RateLimitError
for attempt in range(5):
try:
output = provider.generate(llm_input)
break
except RateLimitError:
if attempt == 4:
raise
time.sleep(2 ** attempt) Prevention
- Limit concurrent Claude requests per key with a semaphore.
- Use prompt caching (cache_creation_input_tokens) to reduce input-token pressure.
- Back off exponentially on RateLimitError, not with a fixed delay.
When it happens
Trigger: Calling ClaudeProvider.generate() past Anthropic's per-minute ITPM/OTPM or requests-per-minute limit; the anthropic SDK raises an error whose str() contains '429' or 'rate_limit'.
Common situations: Bursty agentic loops firing many parallel Claude calls; exceeding the rolling 1-hour token bucket on a lower tier; a shared workspace key under multi-tenant load; prompt-caching misses inflating billable input tokens.
Related errors
- {msg}
- {msg}
- Claude requires explicit --claude-scope and --claude-hooks c
- Invalid hooks config at ${hooksSourcePath}: expected "hooks"
- Invalid Claude scope: ${claudeScope}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/83bbbc4284aaec9c.
Report an issue: GitHub.