PrefectHQ/fastmcp · error · ValueError

Failed to convert JWK to PEM: {e}

Error message

Failed to convert JWK to PEM: {e}

What it means

A matching JWK entry was found in the JWKS, but converting it to a PEM public key failed (`_jwk_to_pem` raised a jose error, TypeError, or ValueError). This means the JWK entry itself is malformed or of an unsupported type/curve.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/cimd.py:700

            if kid and key.get("kid") == kid:
                matching_key = key
                break

        if not matching_key:
            # If no kid match, try first key as fallback
            if len(keys) == 1:
                matching_key = keys[0]
                self.logger.warning(
                    "No matching kid in JWKS, using single available key"
                )
            else:
                raise ValueError(f"No matching key found for kid={kid} in JWKS")

        # Convert JWK to PEM
        try:
            return _jwk_to_pem(matching_key)
        except (JoseError, TypeError, ValueError) as e:
            raise ValueError(f"Failed to convert JWK to PEM: {e}") from e


class CIMDClientManager:
    """Manages all CIMD client operations for OAuth proxy.

    This class encapsulates:
    - CIMD client detection
    - Document fetching and validation
    - Synthetic OAuth client creation
    - Private key JWT assertion validation

    This allows the OAuth proxy to delegate all CIMD-specific logic to a
    single, focused manager class.
    """

    def __init__(
        self,
        enable_cimd: bool = True,

View on GitHub (pinned to 1f02114297)

Solutions

  1. Regenerate the JWK entry with a proper tool so base64url components are complete and correct (e.g. publish the RSA `n`/`e` from the actual signing key)
  2. Use a supported key type — RSA or an EC curve supported by the installed cryptography backend
  3. Ensure the JWKS entry is a public asymmetric key (not `kty: oct`), including public components
  4. Compare the JWKS entry against the issuer's canonical published JWKS and replace the malformed entry

Example fix

// before (truncated modulus)
{"kty": "RSA", "kid": "key-1", "n": "abc", "e": "AQAB"}
// after (full base64url modulus)
{"kty": "RSA", "kid": "key-1", "n": "0vx7agoebGcQS...full modulus...", "e": "AQAB"}
Defensive patterns

Strategy: validation

Validate before calling

from jose import jwt as jose_jwt
from jose.exceptions import JOSEError
for k in jwks["keys"]:
    try:
        jose_jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(k))
    except (JOSEError, ValueError) as e:
        raise ValueError(f"Malformed JWK {k.get('kid')}: {e}")

Type guard

def is_wellformed_rsa_jwk(k: dict) -> bool:
    return k.get("kty") == "RSA" and bool(k.get("n")) and bool(k.get("e"))

Try / catch

try:
    key = extract_public_key(jwks, kid)
except ValueError as e:
    if "Failed to convert JWK to PEM" in str(e):
        logger.error("JWKS entry malformed; refetch/repair JWKS for kid=%s", kid)
    raise

Prevention

When it happens

Trigger: The selected JWK has missing/invalid fields (e.g. bad `n`/`e` base64url for RSA, unsupported `kty` or `crv`, truncated key material), causing the python-jose conversion to fail while extracting the public key in `_extract_public_key_from_jwks`.

Common situations: Hand-authored JWKS with copy-paste-corrupted base64url values; unsupported key type (EC curve not supported by the crypto backend, OKP/Ed25519 keys); a JWK containing a private-only or symmetric (`kty: oct`) key where an RSA/EC public key is required.

Related errors


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