{"record":{"id":"7a3d11dbc38961d2","repo":"affaan-m/ECC","slug":"msg","errorCode":null,"errorMessage":"{msg}","messagePattern":"\\{msg\\}","errorType":"exception","errorClass":"AuthenticationError","httpStatus":401,"severity":"error","filePath":"src/llm/providers/astraflow.py","lineNumber":116,"sourceCode":"            usage = None\n            if response.usage:\n                usage = {\n                    \"prompt_tokens\": response.usage.prompt_tokens,\n                    \"completion_tokens\": response.usage.completion_tokens,\n                    \"total_tokens\": response.usage.total_tokens,\n                }\n\n            return LLMOutput(\n                content=choice.message.content or \"\",\n                tool_calls=tool_calls,\n                model=response.model,\n                usage=usage,\n                stop_reason=choice.finish_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=self.provider_type) from e\n            if \"429\" in msg or \"rate_limit\" in msg.lower():\n                raise RateLimitError(msg, provider=self.provider_type) from e\n            if \"context\" in msg.lower() and \"length\" in msg.lower():\n                raise ContextLengthError(msg, provider=self.provider_type) 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.api_key)\n\n    def get_default_model(self) -> str:\n        return self.default_model\n\n\nclass AstraflowProvider(_AstraflowBaseProvider):\n    \"\"\"UModelVerse global endpoint using OpenAI-compatible chat completions.\"\"\"","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/src/llm/providers/astraflow.py#L98-L134","documentation":"AstraflowProvider.generate() catches every exception from the chat.completions call and string-matches the message. If '401' or 'authentication' appears in the message it re-raises as AuthenticationError(msg, provider=ASTRAFLOW). The message body is the underlying exception's str(). The provider re-uses the same msg variable for all three classified errors (auth, rate-limit, context-length), so the type is the discriminator, not the text.","triggerScenarios":"ASTRAFLOW_API_KEY (or ASTRAFLOW_CN_API_KEY for the CN provider) is missing, wrong, expired, or revoked; the key does not have permission for the requested model; the base_url points at a region where the key is not provisioned.","commonSituations":"Forgotten env var in CI; copy-paste typo in the key; rotated key not updated in deployment; using a CN key against the global endpoint or vice versa.","solutions":["Confirm ASTRAFLOW_API_KEY is exported and matches the dashboard value (no surrounding quotes or whitespace).","For the CN provider, set ASTRAFLOW_CN_API_KEY and use ASTRAFLOW_CN_BASE_URL.","Call provider.validate_config() before generate() — it returns bool(self.api_key) and catches a missing key cheaply.","If the key is correct, verify it is provisioned for the requested model on the provider dashboard."],"exampleFix":"# before\nprovider = AstraflowProvider()\nout = provider.generate(llm_input)\n\n# after\nprovider = AstraflowProvider()\nif not provider.validate_config():\n    raise SystemExit('ASTRAFLOW_API_KEY not set')\ntry:\n    out = provider.generate(llm_input)\nexcept AuthenticationError as e:\n    raise SystemExit(f'Auth failed for {e.provider}: check ASTRAFLOW_API_KEY') from e","handlingStrategy":"try-catch","validationCode":"from llm.providers.astraflow import AstraflowProvider\n\nprovider = AstraflowProvider()\nif not provider.validate_config():\n    raise SystemExit('ASTRAFLOW_API_KEY not set or empty')","typeGuard":"from llm.core.interface import AuthenticationError, LLMError\n\ndef is_auth_error(e: BaseException) -> bool:\n    return isinstance(e, AuthenticationError) or (\n        isinstance(e, LLMError) and e.code in {'401', 'authentication'}\n    )","tryCatchPattern":"from llm.core.interface import AuthenticationError\ntry:\n    out = provider.generate(llm_input)\nexcept AuthenticationError as e:\n    raise SystemExit(f'auth failed for {e.provider}; check ASTRAFLOW_API_KEY') from e","preventionTips":["Always call provider.validate_config() before the first generate().","Use the CN provider (AstraflowCNProvider) with ASTRAFLOW_CN_API_KEY for the api.modelverse.cn endpoint.","Rotate keys on a schedule and store them via a secret manager, not plaintext env files."],"tags":["authentication","llm","provider","astraflow","openai-compatible"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}