PrefectHQ/fastmcp · warning · IdentityAssertionError
OIDC discovery for issuer {issuer!r} recently failed; backin
Error message
OIDC discovery for issuer {issuer!r} recently failed; backing off What it means
OIDC discovery (.well-known/openid-configuration) for a trusted issuer failed recently, and the manager enforces a cooldown per issuer before retrying. Within that window `_discover_jwks_uri` raises `IdentityAssertionError` immediately instead of re-fetching, protecting the authorization server from repeated slow/timing-out discovery requests.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/identity_assertion.py:273
self._cleanup_expired_jtis()
self._last_cleanup = now
async def _discover_jwks_uri(self, issuer: str) -> str:
"""Discover an issuer's JWKS URI via OIDC discovery.
Fetches ``{issuer}/.well-known/openid-configuration`` and returns its
``jwks_uri``. Trusted issuers are operator-configured, so this uses a
plain fetch (consistent with how operator-configured JWKS URIs are
treated elsewhere, including localhost issuers in development).
"""
lock = self._discovery_locks.setdefault(issuer, asyncio.Lock())
async with lock:
failed_at = self._discovery_failures.get(issuer)
if (
failed_at is not None
and time.monotonic() - failed_at < self._discovery_failure_cooldown
):
raise IdentityAssertionError(
f"OIDC discovery for issuer {issuer!r} recently failed; backing off"
)
return await self._fetch_discovery(issuer)
async def _fetch_discovery(self, issuer: str) -> str:
"""Perform the actual discovery fetch; caller holds the issuer lock."""
config_url = issuer.rstrip("/") + "/.well-known/openid-configuration"
try:
async with httpx2.AsyncClient() as client:
response = await client.get(config_url, timeout=10.0)
response.raise_for_status()
body = response.json()
except (httpx2.HTTPError, ValueError) as e:
self._discovery_failures[issuer] = time.monotonic()
raise IdentityAssertionError(
f"OIDC discovery for issuer {issuer!r} failed: {e}"
) from e
if not isinstance(body, dict):View on GitHub (pinned to 1f02114297)
Solutions
- Wait for the cooldown window to pass and retry — discovery will be attempted again automatically
- Verify the issuer URL is correct and that `https://<issuer>/.well-known/openid-configuration` is reachable from the server
- Fix network egress/DNS/TLS issues between your server and the issuer (proxy settings, firewall rules)
- If the issuer has no discovery document, configure its JWKS URI statically instead of relying on discovery
Example fix
// before (issuer unreachable / typo) trusted_issuers=["https://issuer.example.co"] // after type: curl https://issuer.example.com/.well-known/openid-configuration # verify then use trusted_issuers=["https://issuer.example.com"]
Defensive patterns
Strategy: retry
Validate before calling
import httpx
r = httpx.get(f"{issuer.rstrip('/')}/.well-known/openid-configuration", timeout=5)
r.raise_for_status()
assert "jwks_uri" in r.json(), "discovery doc missing jwks_uri" Type guard
def issuer_discoverable(issuer: str) -> bool:
try:
r = httpx.get(f"{issuer.rstrip('/')}/.well-known/openid-configuration", timeout=5)
return r.ok and "jwks_uri" in r.json()
except httpx.HTTPError:
return False Try / catch
try:
verifier = await manager._get_verifier(issuer)
except IdentityAssertionError as e:
if "backing off" in str(e):
await asyncio.sleep(manager._discovery_failure_cooldown)
verifier = await manager._get_verifier(issuer)
else:
raise Prevention
- Verify each trusted issuer's well-known endpoint is reachable from the server before adding it
- Monitor outbound connectivity/DNS/TLS to issuer domains
- Configure the JWKS URI statically for issuers without discovery support
- Treat the cooldown as a signal to fix the underlying issuer reachability, not just to retry faster
When it happens
Trigger: `_get_verifier` (via `_discover_jwks_uri`) is called for an issuer whose previous discovery fetch failed within `_discovery_failure_cooldown` seconds — e.g. the issuer's discovery endpoint was down or timed out moments earlier and another token exchange arrives.
Common situations: Issuer's well-known endpoint temporarily unreachable (network outage, DNS failure, TLS problems); issuer misconfigured in trusted_issuers (wrong URL, no discovery document); rate limiting or firewall blocking the server's outbound requests; flaky issuer during a deploy window.
Related errors
- OIDC discovery for issuer {issuer!r} failed: {e}
- OIDC discovery document for issuer {issuer!r} is not a JSON
- OIDC discovery document for issuer {issuer!r} has no jwks_ur
- Missing required configuration metadata: {attr}
- Upstream request timed out, please retry
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/9bbcba257bdab6ab.
Report an issue: GitHub.