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
- Restart the flow: call authorize_device again to get a fresh device authorization with a new expires_in
- Complete browser approval promptly after starting login
- 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
- Open the browser automatically (open_browser=True) to reduce approval latency
- Complete approval soon after login starts; restart if the prompt sits idle
- Check expires_in from the DeviceAuthorization before starting long-running work
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
- The device authorization request was denied
- Device authorization failed
- OAuth callback timed out after {self._callback_timeout} seco
- User server did not start on port {mcp_port}
- OAuth client not found - cached credentials may be stale
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/b4ac0c0a14e02b97.
Report an issue: GitHub.