{"record":{"id":"569a9cc7fc0718dd","repo":"xtekky/gpt4free","slug":"missing-tokens-in-response-569a9c","errorCode":null,"errorMessage":"Missing tokens in response","messagePattern":"Missing tokens in response","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"g4f/Provider/needs_auth/GeminiCLI.py","lineNumber":1153,"sourceCode":"            }\n\n            async with session.post(\n                \"https://oauth2.googleapis.com/token\",\n                data=token_data,\n                headers={\"Content-Type\": \"application/x-www-form-urlencoded\"},\n            ) as resp:\n                if not resp.ok:\n                    error_text = await resp.text()\n                    raise RuntimeError(f\"Token exchange failed: {error_text}\")\n\n                token_response = await resp.json()\n\n            access_token = token_response.get(\"access_token\")\n            refresh_token = token_response.get(\"refresh_token\")\n            expires_in = token_response.get(\"expires_in\", 3600)\n\n            if not access_token or not refresh_token:\n                raise RuntimeError(\"Missing tokens in response\")\n\n            # Get user info\n            email = None\n            async with session.get(\n                \"https://www.googleapis.com/oauth2/v1/userinfo?alt=json\",\n                headers={\"Authorization\": f\"Bearer {access_token}\"},\n            ) as resp:\n                if resp.ok:\n                    user_info = await resp.json()\n                    email = user_info.get(\"email\")\n\n        expires_at = int((start_time + expires_in) * 1000)  # milliseconds\n\n        return {\n            \"access_token\": access_token,\n            \"refresh_token\": refresh_token,\n            \"expiry_date\": expires_at,\n            \"email\": email,","sourceCodeStart":1135,"sourceCodeEnd":1171,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/needs_auth/GeminiCLI.py#L1135-L1171","documentation":"Google's token endpoint returned HTTP 200 but the JSON was missing access_token or refresh_token. A successful code exchange always returns both (refresh_token because the flow requests offline access), so this indicates an anomalous response: an intercepted or HTML body, or an error-shaped 200.","triggerScenarios":"token_response.get('access_token') or token_response.get('refresh_token') is falsy after a 200 from oauth2.googleapis.com/token in exchange_code_for_tokens.","commonSituations":"Proxy or TLS interception replacing the response; an API behavior change; a rare Google-side anomaly.","solutions":["Log the raw token_response to inspect what was actually returned","Retry with a brand-new authorization code from a fresh login attempt","Check for proxy or captive-portal interference on oauth2.googleapis.com","Update g4f if the token endpoint contract changed"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    tokens = await GeminiCLI.exchange_code_for_tokens(code, state)\nexcept RuntimeError as e:\n    if \"Missing tokens in response\" in str(e):\n        # anomalous 200; restart the whole login flow\n        tokens = await GeminiCLI.login()","preventionTips":["Log the raw token response when this fires to distinguish interception from API change","Restart the whole login flow rather than retrying the same code"],"tags":["oauth","gemini","login","response-parsing","network"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}