{"record":{"id":"135d140d1ef1fc5b","repo":"BerriAI/litellm","slug":"token-exchange-failed-exc","errorCode":null,"errorMessage":"Token exchange failed: {exc}","messagePattern":"Token exchange failed: (.+?)","errorType":"exception","errorClass":"GetAccessTokenError","httpStatus":null,"severity":"error","filePath":"litellm/llms/chatgpt/authenticator.py","lineNumber":267,"sourceCode":"        try:\n            client: Final = _get_httpx_client()\n            redirect_uri: Final = f\"{CHATGPT_AUTH_BASE}/deviceauth/callback\"\n            body: Final = (\n                \"grant_type=authorization_code\"\n                f\"&code={code_data['authorization_code']}\"\n                f\"&redirect_uri={redirect_uri}\"\n                f\"&client_id={CHATGPT_CLIENT_ID}\"\n                f\"&code_verifier={code_data['code_verifier']}\"\n            )\n            resp: Final = client.post(\n                CHATGPT_OAUTH_TOKEN_URL,\n                headers={\"Content-Type\": \"application/x-www-form-urlencoded\"},\n                content=body,\n            )\n            resp.raise_for_status()\n            data: Final = resp.json()\n        except httpx.HTTPStatusError as exc:\n            raise GetAccessTokenError(\n                message=f\"Token exchange failed: {exc}\",\n                status_code=exc.response.status_code,\n            )\n        except Exception as exc:\n            raise GetAccessTokenError(\n                message=f\"Token exchange failed: {exc}\",\n                status_code=400,\n            )\n\n        if not all(key in data for key in (\"access_token\", \"refresh_token\", \"id_token\")):\n            raise GetAccessTokenError(\n                message=f\"Token exchange response missing fields: {data}\",\n                status_code=400,\n            )\n        return {\n            \"access_token\": data[\"access_token\"],\n            \"refresh_token\": data[\"refresh_token\"],\n            \"id_token\": data[\"id_token\"],","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/chatgpt/authenticator.py#L249-L285","documentation":"GetAccessTokenError raised in the final step of the ChatGPT device flow: the POST to CHATGPT_OAUTH_TOKEN_URL exchanging the authorization code for tokens returned an HTTP error status (raise_for_status fired). The upstream status code is attached and the httpx error text is embedded, so the exact failure (e.g. 400 invalid_grant, 401 bad client) is visible in the message.","triggerScenarios":"Exchanging the one-time authorization code at the token endpoint after the user approved; the code is expired/reused (400), the client_id/redirect mismatch (400/401), or the token endpoint is erroring (5xx).","commonSituations":"Delays between approval and exchange letting the short-lived code expire; retrying the flow mid-exchange so the code is consumed twice; litellm version whose client_id or redirect_uri no longer matches OpenAI's registered app.","solutions":["Restart the login flow to obtain a fresh authorization code and complete it without pauses","Upgrade litellm so CHATGPT_CLIENT_ID and the redirect URI match the current OAuth app registration","Read the embedded status/text: 400 invalid_grant means stale code, 5xx means retry later","Avoid running two device logins concurrently, which can race the code exchange"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from litellm.llms.chatgpt.authenticator import GetAccessTokenError\n\ntry:\n    tokens = authenticator.login()\nexcept GetAccessTokenError as e:\n    if \"Token exchange failed\" in str(e) and e.status_code in (400, 401):\n        print(\"Authorization code rejected (stale or app mismatch). Restart login; upgrade litellm if it repeats.\")\n    elif e.status_code >= 500:\n        time.sleep(10)\n        tokens = authenticator.login()\n    else:\n        raise","preventionTips":["Complete approval promptly so the one-time code does not expire before exchange","Never retry the exchange step alone; always restart the full device flow","Keep litellm current so client_id/redirect stay aligned with the registered OAuth app"],"tags":["chatgpt","oauth","token-exchange","http-status","auth"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}