{"record":{"id":"3e3cce725fb782a9","repo":"xtekky/gpt4free","slug":"missing-tokens-in-response","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/Antigravity.py","lineNumber":657,"sourceCode":"                \"https://oauth2.googleapis.com/token\",\n                data=token_data,\n                headers={\n                    \"Content-Type\": \"application/x-www-form-urlencoded\",\n                    \"User-Agent\": \"google-api-nodejs-client/10.3.0\",\n                },\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            # Discover project ID if not provided\n            effective_project_id = project_id\n            if not effective_project_id:\n                effective_project_id = await cls._fetch_project_id(\n                    session, access_token\n                )\n","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/needs_auth/Antigravity.py#L639-L675","documentation":"The token endpoint returned 200 but the JSON lacked access_token or refresh_token. access_token absence is anomalous (see error 101), but a missing refresh_token is the classic Google OAuth outcome when the consent grant did not request/force offline access — Google only issues a refresh_token the first time the user consents, unless approval_prompt=force / prompt=consent is used.","triggerScenarios":"Authorization request built without access_type=offline or without forcing re-consent, so Google returns only an access token; or the user had previously granted and Google suppresses the refresh_token on subsequent grants.","commonSituations":"Re-running login after revoking only the token (not the app grant) in the Google account; authorization URL parameters altered to drop access_type=offline; SSO/Workspace policies restricting offline access.","solutions":["Revoke the app in Google Account > Security > Third-party access, then re-run login so Google issues a fresh refresh_token.","Ensure the authorization URL includes access_type=offline and prompt=consent (as built by build_authorization_url) so a refresh token is always returned.","Do not reuse an authorization code from an earlier session; start a new flow."],"exampleFix":"# before: silent re-consent returns no refresh_token\nurl = \"https://accounts.google.com/o/oauth2/v2/auth?...&response_type=code\"  # no offline access\n\n# after: force offline access and fresh consent\nurl = (\"https://accounts.google.com/o/oauth2/v2/auth?\"\n       \"...&response_type=code&access_type=offline&prompt=consent\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    tokens = await Antigravity.exchange_code_for_tokens(code, state)\nexcept RuntimeError as e:\n    if \"Missing tokens in response\" in str(e):\n        # most often missing refresh_token: revoke app grant, re-login with prompt=consent\n        raise","preventionTips":["Authorize with access_type=offline and prompt=consent so Google always returns a refresh_token","Revoke the app in Google Account settings before re-linking to force a fresh refresh_token","Validate the parsed token_response contains both keys before persisting credentials"],"tags":["oauth","refresh-token","google","antigravity"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}