{"record":{"id":"d8fdb82228c5a1c6","repo":"PrefectHQ/fastmcp","slug":"no-access-token-available-cannot-perform-obo-exch","errorCode":null,"errorMessage":"No access token available. Cannot perform OBO exchange.","messagePattern":"No access token available\\. Cannot perform OBO exchange\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/providers/azure.py","lineNumber":851,"sourceCode":"    \"\"\"Dependency that performs OBO token exchange for Microsoft Entra.\n\n    Uses azure.identity's OnBehalfOfCredential for async-native OBO,\n    with automatic token caching and refresh. Credentials are cached on\n    the AzureProvider so repeated tool calls reuse existing credentials\n    and benefit from the Azure SDK's internal token cache.\n    \"\"\"\n\n    def __init__(self, scopes: list[str]):\n        self.scopes = scopes\n\n    async def __aenter__(self) -> str:\n        _require_azure_identity(\"EntraOBOToken\")\n\n        from fastmcp.server.dependencies import get_access_token, get_server\n\n        access_token = get_access_token()\n        if access_token is None:\n            raise RuntimeError(\n                \"No access token available. Cannot perform OBO exchange.\"\n            )\n\n        server = get_server()\n        azure_provider = _find_azure_provider(server.auth)\n        if azure_provider is None:\n            raise RuntimeError(\n                \"EntraOBOToken requires an AzureProvider as the auth provider. \"\n                f\"Current provider: {type(server.auth).__name__}\"\n            )\n\n        credential = await azure_provider.get_obo_credential(\n            user_assertion=access_token.token,\n        )\n\n        result = await credential.get_token(*self.scopes)\n        return result.token\n","sourceCodeStart":833,"sourceCodeEnd":869,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/providers/azure.py#L833-L869","documentation":"EntraOBOToken's __aenter__ exchanges the current request's access token for a downstream token via OBO. It reads the token from FastMCP's request-scoped context via get_access_token(); if no token is in context (None), there is nothing to exchange, so it raises RuntimeError.","triggerScenarios":"Using `async with EntraOBOToken(...) as t:` in code that runs outside an authenticated MCP request — e.g. at server startup, in a background task, in a tool without auth, or when the auth middleware did not populate the context.","commonSituations":"Calling OBO from a non-request context (startup/shutdown hooks, scheduled jobs); running a tool while the server has no auth provider wired for the incoming request; testing tools outside the FastMCP request lifecycle.","solutions":["Only use EntraOBOToken inside request-scoped code (tool/resource handlers) where an authenticated access token exists.","Ensure the server's auth provider is configured and the client is actually sending a valid token so get_access_token() returns a token.","For non-request contexts, obtain the user assertion explicitly and call provider.get_obo_credential(user_assertion=...) instead of relying on context."],"exampleFix":"// before\n@app.on_event(\"startup\")\nasync def warm():\n    async with EntraOBOToken(scopes=[\"api\"]) as t: ...  # no request context\n// after\n@.tool\nasync def my_tool():\n    async with EntraOBOToken(scopes=[\"api\"]) as t: ...  # inside request context","handlingStrategy":"try-catch","validationCode":"from fastmcp.server.dependencies import get_access_token\ndef assert_request_token_available() -> bool:\n    return get_access_token() is not None","typeGuard":"def has_access_token() -> bool:\n    from fastmcp.server.dependencies import get_access_token\n    t = get_access_token()\n    return t is not None and bool(t.token)","tryCatchPattern":"try:\n    async with EntraOBOToken(scopes=[\"api\"]) as t:\n        ...\nexcept RuntimeError as e:\n    if \"No access token\" in str(e):\n        raise RuntimeError(\"EntraOBOToken used outside an authenticated request context\") from e\n    raise","preventionTips":["Only enter EntraOBOToken inside tool/resource handlers, never at startup or in background jobs","Require auth middleware so every request carries a token","Add a unit test asserting tools fail fast without a token context"],"tags":["python","azure","obo","context","runtime"],"backgroundTag":"no-access-token-in-context","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}