PrefectHQ/fastmcp · error · TimeoutError

OAuth callback timed out after {self._callback_timeout} seco

Error message

OAuth callback timed out after {self._callback_timeout} seconds

What it means

The OAuth callback handler starts a temporary local HTTP server on the redirect port and waits (anyio.fail_after) for the provider to redirect back with the authorization code. If no callback arrives within _callback_timeout seconds, TimeoutError is raised; the local server is then shut down in the finally block.

Source

Thrown at fastmcp_slim/fastmcp/client/auth/oauth.py:436

        async with anyio.create_task_group() as tg:
            tg.start_soon(server.serve)
            logger.info(
                f"🎧 OAuth callback server started on http://{self._callback_host}:{self.redirect_port}"
            )

            try:
                with anyio.fail_after(self._callback_timeout):
                    await result_ready.wait()
                    if result.error:
                        raise result.error
                    # `result.code` is set once `result_ready` fires without error.
                    return AuthorizationCodeResult(
                        code=result.code,  # type: ignore[arg-type]  # ty:ignore[invalid-argument-type]
                        state=result.state,
                        iss=result.iss,
                    )
            except TimeoutError as e:
                raise TimeoutError(
                    f"OAuth callback timed out after {self._callback_timeout} seconds"
                ) from e
            finally:
                server.should_exit = True
                await anyio.sleep(0.1)  # Allow server to shut down gracefully
                tg.cancel_scope.cancel()

        raise RuntimeError("OAuth callback handler could not be started")

    async def async_auth_flow(
        self, request: httpx2.Request
    ) -> AsyncGenerator[httpx2.Request, httpx2.Response]:
        """HTTPX auth flow with automatic retry on stale cached credentials.

        If the OAuth flow fails due to invalid/stale client credentials,
        clears the cache and retries once with fresh registration.
        """
        if not self._bound:

View on GitHub (pinned to 1f02114297)

Solutions

  1. Ensure you are on an environment with an interactive browser, or complete the authorization URL manually (copy the logged 'OAuth authorization URL' into a browser that can reach the server).
  2. Increase the callback timeout when constructing OAuth(...) (callback timeout parameter).
  3. Verify the redirect port matches what the provider/registration expects and that localhost on that port is reachable/firewalled-open.
  4. Retry the flow; a fresh callback server is started on each attempt.

Example fix

// before: default timeout too short for slow SSO
auth = OAuth(mcp_url='https://mcp.example.com/mcp')
// after: longer window for the user to finish logging in
auth = OAuth(mcp_url='https://mcp.example.com/mcp', callback_timeout=300)
Defensive patterns

Strategy: retry

Try / catch

import timeout as _  # noqa — pattern only
try:
    async with client:
        await client.list_tools()
except TimeoutError as e:
    print('OAuth callback timed out:', e)
    # retry with a larger callback_timeout or complete the flow interactively

Prevention

When it happens

Trigger: Calling client.call_tool/list_tools with auth='oauth' or OAuth(...) when the user never completes the browser sign-in, the browser fails to open (headless environment), the redirect port is wrong/blocked so the provider's redirect can't reach localhost, or the callback server took longer than the timeout to receive the result.

Common situations: Running in SSH/CI/headless containers where webbrowser.open does nothing; firewall blocking the localhost redirect port; user closing the browser tab; slow SSO login exceeding the timeout; redirect URI registered with a different port than the one the callback server listens on.

Understand the failure class

Related errors


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