PrefectHQ/fastmcp · error · IdentityAssertionError
OIDC discovery document for issuer {issuer!r} is not a JSON
Error message
OIDC discovery document for issuer {issuer!r} is not a JSON object What it means
The discovery endpoint returned a 200 response with valid JSON, but the parsed body is not a JSON object (e.g. an array, string, or number). FastMCP raises before calling .get() so a misbehaving IdP yields invalid_grant rather than an AttributeError/500 on every token exchange.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/identity_assertion.py:295
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):
# Valid JSON that isn't an object (e.g. `[]` or a bare string) —
# guard before .get() so a misbehaving discovery endpoint maps to
# invalid_grant, not a 500 on every subsequent exchange.
raise IdentityAssertionError(
f"OIDC discovery document for issuer {issuer!r} is not a JSON object"
)
jwks_uri = body.get("jwks_uri")
if not jwks_uri or not isinstance(jwks_uri, str):
raise IdentityAssertionError(
f"OIDC discovery document for issuer {issuer!r} has no jwks_uri"
)
return jwks_uri
async def _get_verifier(self, issuer: str) -> JWTVerifier:
from fastmcp.server.auth.providers.jwt import JWTVerifier as _JWTVerifier
verifier = self._verifiers.get(issuer)
if verifier is not None:
return verifier
jwks_uri = (self.config.jwks_uris or {}).get(issuer)View on GitHub (pinned to 1f02114297)
Solutions
- curl the issuer's discovery URL and inspect the raw body; fix the IdP/gateway so it returns a proper JSON object with issuer/jwks_uri fields.
- Confirm you are hitting the OIDC discovery endpoint, not some other endpoint (wrong well-known path configured in the issuer URL).
- If the IdP is broken and cannot be fixed, configure the verifier with an explicit JWKS URI instead of discovery.
- Check for transparent proxies that replace 404 bodies with JSON error payloads, and fix proxy routing.
Example fix
// before (bad discovery response)
[]
// after (correct discovery response shape)
{"issuer": "https://idp.example.com", "jwks_uri": "https://idp.example.com/.well-known/jwks.json"} Defensive patterns
Strategy: validation
Validate before calling
import json, httpx
body = httpx.get(issuer + '/.well-known/openid-configuration').json()
if not isinstance(body, dict):
raise ValueError('discovery document is not a JSON object') Type guard
def is_dict(x) -> bool:
return isinstance(x, dict) Try / catch
try:
await provider.validate(assertion)
except IdentityAssertionError as e:
if 'not a JSON object' in str(e):
log.error('IdP discovery doc malformed; check IdP metadata endpoint')
raise Prevention
- Verify discovery output with jq during IdP setup
- Beware proxies that replace error pages with JSON strings
- Use well-known, compliant OIDC providers
- Add an integration test fetching discovery at startup
When it happens
Trigger: validate() on an assertion whose issuer's discovery document parses to a non-dict JSON value — the guard `if not isinstance(body, dict)` fires right after response.json() succeeds.
Common situations: IdP behind a misconfigured gateway/proxy returning a JSON-encoded error string; a custom or buggy OIDC provider whose metadata endpoint emits `[]`; mocking/placeholder endpoints in staging.
Related errors
- OIDC discovery for issuer {issuer!r} recently failed; backin
- OIDC discovery for issuer {issuer!r} failed: {e}
- OIDC discovery document for issuer {issuer!r} has no jwks_ur
- Assertion JOSE header must be a JSON object
- Assertion payload is not a JSON object
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/c01f71552a63559c.
Report an issue: GitHub.