{"record":{"id":"26544c5e349558b2","repo":"PrefectHQ/fastmcp","slug":"oauth-callback-timed-out-after-self-callback-tim","errorCode":null,"errorMessage":"OAuth callback timed out after {self._callback_timeout} seconds","messagePattern":"OAuth callback timed out after (.+?) seconds","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/client/auth/oauth.py","lineNumber":436,"sourceCode":"        async with anyio.create_task_group() as tg:\n            tg.start_soon(server.serve)\n            logger.info(\n                f\"🎧 OAuth callback server started on http://{self._callback_host}:{self.redirect_port}\"\n            )\n\n            try:\n                with anyio.fail_after(self._callback_timeout):\n                    await result_ready.wait()\n                    if result.error:\n                        raise result.error\n                    # `result.code` is set once `result_ready` fires without error.\n                    return AuthorizationCodeResult(\n                        code=result.code,  # type: ignore[arg-type]  # ty:ignore[invalid-argument-type]\n                        state=result.state,\n                        iss=result.iss,\n                    )\n            except TimeoutError as e:\n                raise TimeoutError(\n                    f\"OAuth callback timed out after {self._callback_timeout} seconds\"\n                ) from e\n            finally:\n                server.should_exit = True\n                await anyio.sleep(0.1)  # Allow server to shut down gracefully\n                tg.cancel_scope.cancel()\n\n        raise RuntimeError(\"OAuth callback handler could not be started\")\n\n    async def async_auth_flow(\n        self, request: httpx2.Request\n    ) -> AsyncGenerator[httpx2.Request, httpx2.Response]:\n        \"\"\"HTTPX auth flow with automatic retry on stale cached credentials.\n\n        If the OAuth flow fails due to invalid/stale client credentials,\n        clears the cache and retries once with fresh registration.\n        \"\"\"\n        if not self._bound:","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/auth/oauth.py#L418-L454","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Increase the callback timeout when constructing OAuth(...) (callback timeout parameter).","Verify the redirect port matches what the provider/registration expects and that localhost on that port is reachable/firewalled-open.","Retry the flow; a fresh callback server is started on each attempt."],"exampleFix":"// before: default timeout too short for slow SSO\nauth = OAuth(mcp_url='https://mcp.example.com/mcp')\n// after: longer window for the user to finish logging in\nauth = OAuth(mcp_url='https://mcp.example.com/mcp', callback_timeout=300)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"import timeout as _  # noqa — pattern only\ntry:\n    async with client:\n        await client.list_tools()\nexcept TimeoutError as e:\n    print('OAuth callback timed out:', e)\n    # retry with a larger callback_timeout or complete the flow interactively","preventionTips":["Only run browser-based OAuth on machines with an interactive browser; use a different auth path in headless/CI environments.","Increase callback_timeout for slow SSO providers.","Ensure the redirect port is open in the firewall and matches the registered redirect URI.","Tell users to complete the browser login promptly; the local callback server shuts down on timeout."],"tags":["oauth","timeout","callback","browser"],"backgroundTag":"oauth-callback-timeout","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}