PrefectHQ/fastmcp · error · ValueError

Unsupported algorithm {algorithm!r} for issuer {issuer!r}: m

Error message

Unsupported algorithm {algorithm!r} for issuer {issuer!r}: must be one of {supported}

What it means

The per-issuer `algorithms` mapping lets you pin an algorithm per trusted issuer; each value must be one of the asymmetric algorithms JWTVerifier supports. The validator rejects the whole mapping if any issuer's algorithm is unsupported, surfacing config mistakes at load time.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/identity_assertion.py:176

        # anything else (EdDSA, or a typo like RS999) would otherwise surface
        # as a 500 on the first exchange instead of a clean config error now.
        if v is not None and v not in SUPPORTED_ASSERTION_ALGORITHMS:
            supported = ", ".join(sorted(SUPPORTED_ASSERTION_ALGORITHMS))
            raise ValueError(
                f"Unsupported algorithm {v!r} for identity assertion: trusted "
                f"issuers are verified via JWKS, so algorithm must be one of "
                f"{supported}"
            )
        return v

    @field_validator("algorithms")
    @classmethod
    def _validate_algorithms(cls, v: dict[str, str] | None) -> dict[str, str] | None:
        if v is not None:
            for issuer, algorithm in v.items():
                if algorithm not in SUPPORTED_ASSERTION_ALGORITHMS:
                    supported = ", ".join(sorted(SUPPORTED_ASSERTION_ALGORITHMS))
                    raise ValueError(
                        f"Unsupported algorithm {algorithm!r} for issuer "
                        f"{issuer!r}: must be one of {supported}"
                    )
        return v


class IdentityAssertionError(Exception):
    """Raised when an ID-JAG fails validation.

    The message is for server-side logging only; the token endpoint maps this to a
    generic OAuth error response and does not leak the detail to the client.
    """


class IdentityAssertionValidator:
    """Validates ID-JAG assertions for the SEP-990 jwt-bearer grant.

    Reuses :class:`JWTVerifier` for signature, issuer, audience, and expiry checks

View on GitHub (pinned to 1f02114297)

Solutions

  1. Replace the offending issuer's algorithm with a supported one (e.g. "RS256", "ES256")
  2. Drop the per-issuer entry for that issuer so it uses the default algorithm selection
  3. Fix casing/typos so the algorithm name exactly matches a member of SUPPORTED_ASSERTION_ALGORITHMS

Example fix

# before
IdentityAssertionSettings(algorithms={"https://issuer.example": "HS256"})
# after
IdentityAssertionSettings(algorithms={"https://issuer.example": "RS256"})
Defensive patterns

Strategy: validation

Validate before calling

for issuer, alg in (per_issuer_algorithms or {}).items():
    if alg not in SUPPORTED_ASSERTION_ALGORITHMS:
        raise ValueError(f"issuer {issuer!r}: unsupported algorithm {alg!r}")

Type guard

def all_algorithms_supported(m: dict[str, str] | None) -> bool:
    return m is None or all(a in SUPPORTED_ASSERTION_ALGORITHMS for a in m.values())

Try / catch

try:
    settings = IdentityAssertionSettings(algorithms=algorithms)
except ValidationError as e:
    logger.error("Per-issuer algorithm invalid: %s", e)
    raise SystemExit(1)

Prevention

When it happens

Trigger: Passing `algorithms={"https://issuer.example": "HS256"}` or a typo like `"rs526"`/`"EdDSA"` in the per-issuer mapping — `_validate_algorithms` runs during settings model validation.

Common situations: Same as the global algorithm error but scoped to a specific issuer: copied a shared-secret config per issuer, case/typo mistakes in algorithm names, mixed config from multiple providers where one issuer's entry is stale.

Related errors


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