{"record":{"id":"ff74fa1eff3e4b4b","repo":"BerriAI/litellm","slug":"api-key-is-required-for-gemini-api-calls","errorCode":null,"errorMessage":"api_key is required for Gemini API calls","messagePattern":"api_key is required for Gemini API calls","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"litellm/llms/gemini/realtime/transformation.py","lineNumber":135,"sourceCode":"                {\"text_tokens\": 0, \"audio_tokens\": 0},\n            ),\n        )\n        return usage_dict\n\n    def validate_environment(self, headers: dict, model: str, api_key: str | None = None) -> dict:\n        return headers\n\n    def get_complete_url(self, api_base: str | None, model: str, api_key: str | None = None) -> str:\n        \"\"\"\n        Example output:\n        \"BACKEND_WS_URL = \"wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent\"\";\n        \"\"\"\n        if api_base is None:\n            api_base = \"wss://generativelanguage.googleapis.com\"\n        if api_key is None:\n            api_key = get_api_key_from_env()\n        if api_key is None:\n            raise ValueError(\"api_key is required for Gemini API calls\")\n        api_base = api_base.replace(\"https://\", \"wss://\")\n        api_base = api_base.replace(\"http://\", \"ws://\")\n        # WebSocket connections do not support custom HTTP headers in all clients,\n        # so the API key must remain as a query parameter here. This is an accepted\n        # limitation; httpx is not used for WebSocket so MaskedHTTPStatusError\n        # already covers the main leak vector.\n        return f\"{api_base}/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key={api_key}\"\n\n    def map_model_turn_event(self, model_turn: HttpxContentType) -> OpenAIRealtimeEventTypes:\n        \"\"\"\n        Map the model turn event to the OpenAI realtime events.\n\n        Returns either:\n        - response.text.delta - model_turn: {\"parts\": [{\"text\": \"...\"}]}\n        - response.audio.delta - model_turn: {\"parts\": [{\"inlineData\": {\"mimeType\": \"audio/pcm\", \"data\": \"...\"}}]}\n\n        Assumes parts is a single element list.\n        \"\"\"","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/gemini/realtime/transformation.py#L117-L153","documentation":"Raised in GeminiRealtimeConfig.get_complete_url when constructing the WebSocket URL for Gemini realtime sessions. The key is resolved from the explicit api_key argument, then get_api_key_from_env(); if both are None a ValueError is thrown because the key must be embedded as a ?key= query parameter on the wss:// URL. Fails before the socket is ever opened.","triggerScenarios":"Starting a realtime session (e.g. litellm.realtime with model gemini-2.0-flash-live or similar) with api_key=None/empty and no GEMINI_API_KEY/GOOGLE_API_KEY environment variable set.","commonSituations":"Key env var present in dev shell but missing in the deployed container/service; realtime handler running in a separate worker without the env; typo in env var name; assuming the key from chat-completions config carries over to the realtime path.","solutions":["Set GEMINI_API_KEY or GOOGLE_API_KEY in the environment of the process opening the realtime session.","Pass api_key explicitly to the realtime call: litellm.realtime(model='gemini/...', api_key='AIza...').","Print/verify env resolution (get_api_key_from_env) in the same process before connecting."],"exampleFix":"# before\nawait litellm.arealtime(model='gemini/gemini-2.0-flash-live', listener=handler)\n\n# after\nimport os\nos.environ['GEMINI_API_KEY'] = 'AIza...'\nawait litellm.arealtime(\n    model='gemini/gemini-2.0-flash-live',\n    api_key=os.environ['GEMINI_API_KEY'],\n    listener=handler,\n)\n","handlingStrategy":"validation","validationCode":"import os\n\ndef has_realtime_key() -> bool:\n    return bool(os.environ.get(\"GEMINI_API_KEY\") or os.environ.get(\"GOOGLE_API_KEY\"))\n\nif not has_realtime_key():\n    raise ConfigError(\"GEMINI_API_KEY/GOOGLE_API_KEY required for Gemini realtime\")","typeGuard":null,"tryCatchPattern":"try:\n    await litellm.arealtime(model=\"gemini/gemini-2.0-flash-live\", api_key=key, listener=handler)\nexcept ValueError as e:\n    if \"api_key is required\" in str(e):\n        abort_session_setup(\"missing Gemini realtime key\")\n    raise","preventionTips":["Check env keys in the exact process that opens the WebSocket, not just at deploy time.","Pass api_key explicitly rather than relying on env resolution.","Fail CI smoke tests when realtime key env vars are missing."],"tags":["auth","gemini","realtime","websocket","api-key","config"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}