PrefectHQ/fastmcp · error · CIMDValidationError
CIMD document is not valid JSON
Error message
CIMD document is not valid JSON: {e} What it means
After a successful fetch, the response body must be a JSON object representing the CIMD client metadata document. If json.loads fails (malformed, empty, HTML error page, trailing garbage), fetch() raises CIMDValidationError explaining the body is not valid JSON.
Solutions
- Open the client_id URL and confirm it returns raw JSON (application/json) with a 200 status
- Fix the hosted document's JSON syntax or re-upload without BOM/trailing commas
- Ensure hosting doesn't return an HTML interstitial (login/consent/captcha) for server requests
- Catch CIMDValidationError and return invalid_client to the requesting party
Example fix
try:
doc = await fetcher.get_client(client_id)
except CIMDValidationError as e:
if "not valid JSON" in str(e):
logger.error("CIMD URL %s does not serve JSON", client_id)
raise Defensive patterns
Strategy: validation
Validate before calling
import json, httpx
async def serves_json(client_id_url: str) -> bool:
r = await client.get(client_id_url)
if "json" not in r.headers.get("content-type", ""):
return False
try:
json.loads(r.content)
return True
except json.JSONDecodeError:
return False Type guard
def is_json_object(body: bytes) -> bool:
try:
return isinstance(json.loads(body), dict)
except (json.JSONDecodeError, UnicodeDecodeError):
return False Try / catch
try:
doc = await fetcher.get_client(client_id)
except CIMDValidationError as e:
if "not valid JSON" in str(e):
raise InvalidClientError("metadata endpoint does not serve JSON") from e
raise Prevention
- Serve the document with Content-Type: application/json and no auth interstitial
- Validate JSON syntax before publishing; avoid BOM and trailing commas
- Curl the URL from the server host to confirm what it actually returns
When it happens
Trigger: The URL serving the client_id returns non-JSON content: an HTML 404/login page with status 200, empty body, truncated response, or JSON with syntax errors.
Common situations: Client hosts metadata behind an auth wall returning an HTML page; CDN/error pages served with 200 status; file uploaded with BOM or editor artifacts; wrong Content-Type with HTML fallback content.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- CIMD client_id mismatch: document says
- CIMD document must have jwks_uri or jwks for private_key_jwt
- CIMD redirect_uri must have a host
- Assertion exp too far in future
- Assertion iat is in the future
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/85dd1df81cfc67c8.
Report an issue: GitHub.
Appendix: source
Thrown at fastmcp_slim/fastmcp/server/auth/cimd.py:410
doc=cached.doc,
etag=policy.etag or cached.etag,
last_modified=policy.last_modified or cached.last_modified,
expires_at=policy.expires_at,
freshness_lifetime=policy.freshness_lifetime,
must_revalidate=policy.must_revalidate,
),
)
else:
self._remove_cache_entry(client_id_url)
return cached.doc
now = time.time()
policy = self._parse_cache_policy(response.headers, now)
try:
data = json.loads(response.content)
except json.JSONDecodeError as e:
raise CIMDValidationError(f"CIMD document is not valid JSON: {e}") from e
try:
doc = CIMDDocument.model_validate(data)
except Exception as e:
raise CIMDValidationError(f"Invalid CIMD document: {e}") from e
if str(doc.client_id).rstrip("/") != client_id_url.rstrip("/"):
raise CIMDValidationError(
f"CIMD client_id mismatch: document says '{doc.client_id}' "
f"but was fetched from '{client_id_url}'"
)
# Validate jwks_uri if present (SSRF check for JWKS endpoint)
if doc.jwks_uri:
jwks_uri_str = str(doc.jwks_uri)
try:
await validate_url(jwks_uri_str)
except SSRFError as e:View on GitHub (pinned to 1f02114297)