PrefectHQ/fastmcp · error · ValueError

trusted_issuers entries must be non-empty strings

Error message

trusted_issuers entries must be non-empty strings

What it means

Each entry in `trusted_issuers` must be a non-empty, non-whitespace string. The pydantic `field_validator` `_validate_trusted_issuers` rejects any empty or blank issuer at settings load time, since a blank issuer could never match an assertion's `iss` claim.

Source

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

    )
    access_token_expiry_seconds: int = Field(
        default=300,
        gt=0,
        description=(
            "Lifetime, in seconds, of the short-lived access token minted from an "
            "ID-JAG. SEP-990 relies on the client re-exchanging a fresh assertion, so "
            "this is intentionally short and no refresh token is issued."
        ),
    )

    @field_validator("trusted_issuers")
    @classmethod
    def _validate_trusted_issuers(cls, v: list[str]) -> list[str]:
        if not v:
            raise ValueError("identity_assertion.trusted_issuers must not be empty")
        for issuer in v:
            if not issuer or not issuer.strip():
                raise ValueError("trusted_issuers entries must be non-empty strings")
        return v

    @field_validator("algorithm")
    @classmethod
    def _validate_algorithm(cls, v: str | None) -> str | None:
        # Trusted issuers are verified via JWKS (public keys only), so the
        # algorithm must be one of the asymmetric JWS algorithms JWTVerifier
        # actually supports — HS* (shared-secret) has no JWKS equivalent, and
        # 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

View on GitHub (pinned to 1f02114297)

Solutions

  1. Remove the empty/blank entries so every list element is a full issuer URL
  2. Fix the env-var splitting/parsing that introduced empty strings (filter falsy values)
  3. Correct the config file to remove empty-string issuers

Example fix

# before
TRUSTED_ISSUERS="https://a.com,,https://b.com"  # -> ['', ''] entries
# after
issuers = [u.strip() for u in os.environ["TRUSTED_ISSUERS"].split(",") if u.strip()]
Defensive patterns

Strategy: validation

Validate before calling

issuers = [u for u in raw_issuers if isinstance(u, str) and u.strip()]
assert issuers == raw_issuers, "trusted_issuers contained blank entries"

Type guard

def all_issuers_nonempty(v: list) -> bool:
    return all(isinstance(i, str) and i.strip() for i in v)

Try / catch

try:
    settings = IdentityAssertionSettings(trusted_issuers=raw_issuers)
except ValidationError as e:
    logger.error("Blank trusted_issuers entry: %s", e)
    raise SystemExit(1)

Prevention

When it happens

Trigger: A `trusted_issuers` list containing `""`, `" "`, or None-ish entries — e.g. trailing commas in a comma-separated env var producing empty strings, or `"issuers".split(",")` on a malformed value.

Common situations: Env var like `TRUSTED_ISSUERS="https://a.com,,https://b.com"` producing an empty element; YAML/JSON config with empty string values; template substitution that left a placeholder empty.

Related errors


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