{"record":{"id":"83bbbc4284aaec9c","repo":"affaan-m/ECC","slug":"msg-83bbbc","errorCode":null,"errorMessage":"{msg}","messagePattern":"\\{msg\\}","errorType":"exception","errorClass":"AuthenticationError","httpStatus":401,"severity":"error","filePath":"src/llm/providers/claude.py","lineNumber":128,"sourceCode":"\n            return LLMOutput(\n                content=\"\".join(text_parts),\n                tool_calls=tool_calls or None,\n                model=response.model,\n                usage={\n                    \"input_tokens\": response.usage.input_tokens,\n                    \"output_tokens\": response.usage.output_tokens,\n                    \"cache_creation_input_tokens\": getattr(\n                        response.usage, \"cache_creation_input_tokens\", 0\n                    ),\n                    \"cache_read_input_tokens\": getattr(response.usage, \"cache_read_input_tokens\", 0),\n                },\n                stop_reason=response.stop_reason,\n            )\n        except Exception as e:\n            msg = str(e)\n            if \"401\" in msg or \"authentication\" in msg.lower():\n                raise AuthenticationError(msg, provider=ProviderType.CLAUDE) from e\n            if \"429\" in msg or \"rate_limit\" in msg.lower():\n                raise RateLimitError(msg, provider=ProviderType.CLAUDE) from e\n            if \"context\" in msg.lower() and \"length\" in msg.lower():\n                raise ContextLengthError(msg, provider=ProviderType.CLAUDE) from e\n            raise\n\n    def list_models(self) -> list[ModelInfo]:\n        return self._models.copy()\n\n    def validate_config(self) -> bool:\n        return bool(self.client.api_key)\n\n    def get_default_model(self) -> str:\n        return _DEFAULT_MODEL\n","sourceCodeStart":110,"sourceCodeEnd":143,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/src/llm/providers/claude.py#L110-L143","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"// before\noutput = provider.generate(llm_input)\n\n// after\nimport time\nfrom llm.core.interface import RateLimitError\n\nfor attempt in range(5):\n    try:\n        output = provider.generate(llm_input)\n        break\n    except RateLimitError:\n        if attempt == 4:\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":null,"typeGuard":"from llm.core.interface import RateLimitError\n\ndef is_rate_limit(exc: BaseException) -> bool:\n    return isinstance(exc, RateLimitError)","tryCatchPattern":"import time\nfrom llm.core.interface import RateLimitError\n\nfor attempt in range(5):\n    try:\n        output = provider.generate(llm_input)\n        break\n    except RateLimitError:\n        if attempt == 4:\n            raise\n        time.sleep(2 ** attempt)","preventionTips":["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."],"tags":["claude","rate-limit","llm-provider","anthropic","retry"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}