PrefectHQ/fastmcp · error · DeviceAuthorizationExpiredError

The device authorization request expired

Error message

The device authorization request expired

What it means

poll_device_authorization enforces a local deadline of authorization.expires_in seconds from the start of polling. If the deadline passes before the user approves the request, DeviceAuthorizationExpiredError('The device authorization request expired') is raised immediately before another poll.

Source

Thrown at fastmcp_slim/fastmcp/cli/deploy/authentication.py:47

    """The device authorization request expired."""


async def poll_device_authorization(
    client: HorizonClient,
    authorization: DeviceAuthorization,
    *,
    sleep: Callable[[float], Awaitable[None]] | None = None,
    monotonic: Callable[[], float] = time.monotonic,
) -> SecretStr:
    """Poll at the server interval until the device request completes."""
    sleep = asyncio.sleep if sleep is None else sleep
    deadline = monotonic() + authorization.expires_in
    interval = float(authorization.interval)

    while True:
        remaining = deadline - monotonic()
        if remaining <= 0:
            raise DeviceAuthorizationExpiredError(
                "The device authorization request expired"
            )

        await sleep(min(interval, remaining))
        if monotonic() >= deadline:
            raise DeviceAuthorizationExpiredError(
                "The device authorization request expired"
            )

        result = await client.exchange_device_authorization(authorization.device_code)
        if result.access_token is not None:
            return result.access_token
        if result.error == "authorization_pending":
            continue
        if result.error == "slow_down":
            interval += 5
            continue
        if result.error == "access_denied":

View on GitHub (pinned to 1f02114297)

Solutions

  1. Restart the flow: call authorize_device again to get a fresh device authorization with a new expires_in
  2. Complete browser approval promptly after starting login
  3. Increase the server-provided expires_in if you control the Horizon authorization server

Example fix

// before
token = await poll_device_authorization(client, stale_authorization)  # expired
// after
authorization = await client.create_device_authorization(metadata)
token = await poll_device_authorization(client, authorization)
Defensive patterns

Strategy: try-catch

Validate before calling

import time
if authorization.expires_in <= 0:
    raise RuntimeError("authorization already expired; request a new one")

Try / catch

from fastmcp.cli.deploy.authentication import DeviceAuthorizationExpiredError
try:
    token = await poll_device_authorization(client, authorization)
except DeviceAuthorizationExpiredError:
    authorization = await client.create_device_authorization(metadata)
    token = await poll_device_authorization(client, authorization)

Prevention

When it happens

Trigger: Calling poll_device_authorization (or authorize_device) when monotonic() indicates the local deadline has already elapsed, i.e. the user did not complete browser approval within expires_in seconds.

Common situations: The user left the verification URL open too long, never opened the browser, or a test injects a fake monotonic clock that has advanced past the deadline.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/b4ac0c0a14e02b97. Report an issue: GitHub.