{"record":{"id":"a436ae9a3e3fa48b","repo":"xtekky/gpt4free","slug":"api-key-is-required-a436ae","errorCode":null,"errorMessage":"API key is required.","messagePattern":"API key is required\\.","errorType":"exception","errorClass":"MissingAuthError","httpStatus":null,"severity":"error","filePath":"g4f/providers/base_provider.py","lineNumber":314,"sourceCode":"\nclass AsyncGeneratorProvider(AbstractProvider):\n    \"\"\"\n    Provides asynchronous generator functionality for streaming results.\n    \"\"\"\n\n    supports_stream = True\n    use_stream_timeout = True\n    quota_url = None\n\n    @classmethod\n    async def get_quota(cls, api_key: Optional[str] = None, **kwargs) -> dict:\n        \"\"\"Get the quota information for the API key.\"\"\"\n        if cls.quota_url is None:\n            raise NotImplementedError(\n                f\"{cls.__name__} does not implement get_quota method\"\n            )\n        if not api_key and cls.needs_auth:\n            raise MissingAuthError(\"API key is required.\")\n        headers = {\"authorization\": f\"Bearer {api_key}\"} if api_key else {}\n        async with ClientSession() as session:\n            async with session.get(cls.quota_url, headers=headers) as response:\n                await raise_for_status(response)\n                return await response.json()\n\n    @staticmethod\n    @abstractmethod\n    async def create_async_generator(\n        model: str, messages: Messages, **kwargs\n    ) -> AsyncResult:\n        \"\"\"\n        Abstract method for creating an asynchronous generator.\n\n        Args:\n            model (str): The model to use for creation.\n            messages (Messages): The messages to process.\n            **kwargs: Additional keyword arguments.","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/providers/base_provider.py#L296-L332","documentation":"Raised by AsyncGeneratorProvider.get_quota when no api_key is supplied and the provider class has needs_auth=True. Quota is inherently per-account, so an authenticated provider cannot answer the question anonymously; the check runs before any HTTP request is made, and the quota_url check runs even earlier.","triggerScenarios":"await provider.get_quota() or get_quota(api_key=None) on a provider whose needs_auth is True (paid/keyed providers).","commonSituations":"Reading the key from an env var that is unset in the deployment; forgetting to thread the api_key argument through a wrapper; assuming get_quota works unauthenticated like some free providers do.","solutions":["Pass the key: await provider.get_quota(api_key='...')","Load the key from your secret store (e.g. get_api_key) before calling","Pre-check provider.needs_auth and skip quota checks when you have no key"],"exampleFix":"# before\nquota = await provider.get_quota()\n\n# after\nkey = get_api_key()  # your secret source\nif key or not provider.needs_auth:\n    quota = await provider.get_quota(api_key=key)","handlingStrategy":"validation","validationCode":"if provider.needs_auth and not api_key:\n    raise MissingAuthError(\"supply an api_key before querying quota\")\nquota = await provider.get_quota(api_key=api_key)","typeGuard":"def can_query_quota(provider: type, api_key) -> bool:\n    return not getattr(provider, \"needs_auth\", False) or bool(api_key)","tryCatchPattern":"try:\n    quota = await provider.get_quota(api_key=key)\nexcept MissingAuthError:\n    quota = None  # prompt user for credentials instead of retrying","preventionTips":["Always thread the api_key through to get_quota for authenticated providers","Fail fast on missing keys before any HTTP call","Do not retry MissingAuthError — refresh credentials instead"],"tags":["auth","api-key","quota","provider"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}