{"record":{"id":"5ad7db3ddda3ddc9","repo":"xtekky/gpt4free","slug":"refresh-token-expired-or-invalid","errorCode":null,"errorMessage":"Refresh token expired or invalid","messagePattern":"Refresh token expired or invalid","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"g4f/Provider/qwen/qwenOAuth2.py","lineNumber":177,"sourceCode":"            \"grant_type\": \"refresh_token\",\n            \"refresh_token\": self.credentials[\"refresh_token\"],\n            \"client_id\": QWEN_OAUTH_CLIENT_ID,\n        }\n        async with aiohttp.ClientSession(headers={\"user-agent\": \"\"}) as session:\n            async with session.post(\n                QWEN_OAUTH_TOKEN_ENDPOINT,\n                headers={\n                    \"Content-Type\": \"application/x-www-form-urlencoded\",\n                    \"Accept\": \"application/json\",\n                },\n                data=object_to_urlencoded(body_data),\n            ) as resp:\n                resp_json = await resp.json()\n                if resp.status != 200:\n                    if resp.status == 400:\n                        # Handle token expiration\n                        self.credentials = QwenCredentials()\n                        raise Exception(\"Refresh token expired or invalid\")\n                    raise Exception(f\"Token refresh failed {resp.status}: {resp_json}\")\n                return resp_json\n\n    def isTokenValid(self, credentials: QwenCredentials) -> bool:\n        if not credentials.get(\"expiry_date\"):\n            return False\n        return time.time() * 1000 < credentials[\"expiry_date\"] - TOKEN_REFRESH_BUFFER_MS\n","sourceCodeStart":159,"sourceCodeEnd":185,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/Provider/qwen/qwenOAuth2.py#L159-L185","documentation":"Raised by QwenOAuth2.refreshAccessToken (qwenOAuth2.py:177) when the token endpoint returns HTTP 400 during a refresh_token grant — the OAuth-standard signal that the refresh token is expired, revoked, or invalid. Crucially, the handler first resets self.credentials to an empty QwenCredentials(), so the stored session is destroyed and interactive re-authentication is required.","triggerScenarios":"POST to QWEN_OAUTH_TOKEN_ENDPOINT with grant_type=refresh_token returns 400: refresh token past its lifetime, revoked server-side (password change / logout all devices), or single-use token already consumed by a concurrent process.","commonSituations":"Credentials stored long ago and Qwen's refresh-token TTL elapsed; two g4f processes raced and one consumed the rotating refresh token; account security event revoked grants.","solutions":["Re-run the Qwen device authorization flow — the wiped credentials cannot be recovered","Prevent concurrent refreshes (the shared token manager's file lock exists for this; ensure a single instance refreshes)","Refresh proactively before expiry rather than after, so tokens rotate cleanly","If single-use refresh tokens are the norm, persist the new refresh_token immediately after each refresh"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    resp = await qwen_client.refreshAccessToken()\nexcept Exception as exc:\n    if \"Refresh token expired or invalid\" in str(exc):\n        # credentials were wiped; interactive re-auth is the only path\n        await start_qwen_device_login()\n    else:\n        raise","preventionTips":["Refresh tokens before they expire rather than after","Serialize refreshes across processes to protect rotating refresh tokens","Detect this error early and route users to re-login instead of retry loops"],"tags":["qwen","oauth","refresh-token","reauthentication"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}