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

  1. Open the client_id URL and confirm it returns raw JSON (application/json) with a 200 status
  2. Fix the hosted document's JSON syntax or re-upload without BOM/trailing commas
  3. Ensure hosting doesn't return an HTML interstitial (login/consent/captcha) for server requests
  4. 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

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


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/85dd1df81cfc67c8. Report an issue: GitHub.