BerriAI/litellm · error · GetAccessTokenError

Timed out waiting for device authorization

Error message

Timed out waiting for device authorization

What it means

GetAccessTokenError with status 408 raised after the ChatGPT device-authorization poll loop exhausts its time budget without the user approving the code. The flow printed a verification URL and code, polled until the deadline, and gave up — 'Timed out waiting for device authorization'. It is a user-inaction timeout, not a network failure.

Solutions

  1. Restart the login and complete the verification immediately after the code is printed
  2. Make sure the user_code and verify URL are surfaced to the human (do not swallow stdout in logs)
  3. Approve with the same OpenAI/ChatGPT account the flow expects
  4. If it recurs in automation, pre-provision an API key or auth file instead of interactive device login
Defensive patterns

Strategy: retry

Try / catch

from litellm.llms.chatgpt.authenticator import GetAccessTokenError

try:
    tokens = authenticator.login()
except GetAccessTokenError as e:
    if e.status_code == 408:  # user never approved in time
        print("Login timed out. Restart and approve the code promptly.")
        tokens = authenticator.login()
    else:
        raise

Prevention

When it happens

Trigger: User never visited CHATGPT_DEVICE_VERIFY_URL and entered the user_code before the poll deadline; the code was entered too late or the browser session was on a different account that never approved.

Common situations: Headless/CI runs where nobody sees the printed code; user stepping away from the terminal; codes expiring (they are short-lived by OAuth design); output swallowed by logging so the user never saw the prompt.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/ef42936effce795b. Report an issue: GitHub.

Appendix: source

Thrown at litellm/llms/chatgpt/authenticator.py:243

                    continue
                resp.raise_for_status()
            except httpx.HTTPStatusError as exc:
                status_code = exc.response.status_code if exc.response else None
                if status_code in (403, 404):
                    time.sleep(max(interval, DEVICE_CODE_POLL_SLEEP_SECONDS))
                    continue
                raise GetAccessTokenError(
                    message=f"Polling failed: {exc}",
                    status_code=exc.response.status_code,
                )
            except Exception as exc:
                raise GetAccessTokenError(
                    message=f"Polling failed: {exc}",
                    status_code=400,
                )
            time.sleep(max(interval, DEVICE_CODE_POLL_SLEEP_SECONDS))

        raise GetAccessTokenError(
            message="Timed out waiting for device authorization",
            status_code=408,
        )

    def _exchange_code_for_tokens(self, code_data: dict[str, str]) -> dict[str, str]:
        try:
            client: Final = _get_httpx_client()
            redirect_uri: Final = f"{CHATGPT_AUTH_BASE}/deviceauth/callback"
            body: Final = (
                "grant_type=authorization_code"
                f"&code={code_data['authorization_code']}"
                f"&redirect_uri={redirect_uri}"
                f"&client_id={CHATGPT_CLIENT_ID}"
                f"&code_verifier={code_data['code_verifier']}"
            )
            resp: Final = client.post(
                CHATGPT_OAUTH_TOKEN_URL,
                headers={"Content-Type": "application/x-www-form-urlencoded"},

View on GitHub (pinned to 77b7c6c40c)