PrefectHQ/fastmcp · error · CIMDValidationError
CIMD document is not valid JSON: {e}
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.
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)
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 redirect_uri must have a host: {uri!r}
- CIMD client_id mismatch: document says '{doc.client_id}' but
- CIMD document must have jwks_uri or jwks for private_key_jwt
- CLI state is invalid: {path.name}
- Elicitation responses must be serializable as a JSON object
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/85dd1df81cfc67c8.
Report an issue: GitHub.