langchain-ai/deepagents · error · RuntimeError

Device flow timed out. Try logging in again.

Error message

Device flow timed out. Try logging in again.

What it means

`_run_device_flow` gives up and raises this RuntimeError when the polling loop exhausts its time budget without ever receiving a terminal success or error response — the token poll kept returning `authorization_pending` (or `slow_down`) until the deadline. It is the normal failure mode when the user never completes the browser authorization in time.

Source

Thrown at libs/code/deepagents_code/mcp_auth.py:1982

            try:
                token_response.raise_for_status()
            except httpx.HTTPStatusError as exc:
                msg = (
                    f"Token request failed: HTTP {token_response.status_code} "
                    f"from {token_url}."
                )
                raise RuntimeError(msg) from exc
            try:
                return OAuthToken.model_validate(body)
            except ValidationError as exc:
                msg = (
                    f"Token response from {token_url} is not a valid "
                    f"OAuth token payload: {exc}"
                )
                raise RuntimeError(msg) from exc

    msg = "Device flow timed out. Try logging in again."
    raise RuntimeError(msg)


def format_login_failure(exc: BaseException) -> str:
    """Return a token-safe single-line summary of an OAuth-login exception.

    OAuth handshakes commonly surface as `ExceptionGroup` (anyio task
    groups) or as MCP-SDK errors whose `args`/`repr` may include an
    `OAuthToken`. Never call `str()`/`repr()` on the raw exception for
    display or logging — instead, prefer a known-safe nested
    `MCPReauthRequiredError` message, fall back to the messages of our
    own loopback-related exception types, and degrade to a class-name
    chain for anything else.

    Args:
        exc: Root exception caught from the login worker.

    Returns:
        A user-displayable string that is safe to log and to render.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Re-run the login and complete the browser authorization promptly within the timeout window
  2. Ensure the verification URL is openable in your default browser (check BROWSER env var or open the printed URL manually)
  3. In headless/SSH environments, copy the code and URL to a machine with a browser
  4. Retry the login to get a fresh device code if the previous one expired

Example fix

// before: never opening the printed verification URL
$ dcode mcp login
Device code: ABCD-1234  # ignore, flow times out
// after
$ dcode mcp login
# open https://github.com/login/device immediately and enter ABCD-1234
Defensive patterns

Strategy: retry

Validate before calling

# Verify a browser can open the verification URL before starting login
import webbrowser
if not webbrowser.get():
    print("No browser available; copy the device-code URL to another machine")

Try / catch

try:
    login(server_name)
except RuntimeError as exc:
    if "Device flow timed out" in str(exc):
        print("Authorization not completed in time. Re-run login and approve promptly.")
        login(server_name)  # fresh device code
    raise

Prevention

When it happens

Trigger: The device-flow poll loop runs past its timeout while the user has not yet opened the verification URL and entered the code; the loop exits its `while` and hits the unconditional raise at the end of the function.

Common situations: User ignored the browser prompt; verification URL blocked by browser policy; user entered the code after it expired without noticing; headless/CI environment where no one can complete the interactive step.

Understand the failure class

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/2d401453d2c781b0. Report an issue: GitHub.