PrefectHQ/fastmcp · error · ValueError
Either high_entropy_material or low_entropy_material must be
Error message
Either high_entropy_material or low_entropy_material must be provided
What it means
derive_jwt_key() requires some key material: with neither high_entropy_material nor low_entropy_material provided there is nothing to derive a signing key from, so the function raises ValueError. High-entropy material goes through HKDF, low-entropy through PBKDF2; the 'neither' path falls through to this error at jwt_issuer.py:74.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/jwt_issuer.py:74
info=b"Fernet",
).derive(key_material=high_entropy_material.encode())
return base64.urlsafe_b64encode(derived_key)
if low_entropy_material is not None:
iterations = (
KDF_ITERATIONS_TEST if fastmcp.settings.test_mode else KDF_ITERATIONS
)
pbkdf2 = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
iterations=iterations,
).derive(key_material=low_entropy_material.encode())
return base64.urlsafe_b64encode(pbkdf2)
raise ValueError(
"Either high_entropy_material or low_entropy_material must be provided"
)
class JWTIssuer:
"""Issues and validates FastMCP-signed JWT tokens using HS256.
This issuer creates JWT tokens for MCP clients with proper audience claims,
maintaining OAuth 2.0 token boundaries. Tokens are signed with HS256 using
a key derived from the upstream client secret.
"""
def __init__(
self,
issuer: str,
audience: str,
signing_key: bytes,
):View on GitHub (pinned to 1f02114297)
Solutions
- Set the signing-secret environment variable / config so one material is populated before calling derive_jwt_key
- Check that your .env or secret mount is actually loaded (wrong working directory, missing env_file in deployment)
- Add startup validation that fails fast if neither material is configured, with a message naming the expected variable
- Generate a strong high-entropy secret (e.g. secrets.token_urlsafe(32)) if none exists
Example fix
// before derive_jwt_key(salt=salt) # no material configured // after secret = os.environ["FASTMCP_JWT_SECRET"] derive_jwt_key(high_entropy_material=secret, salt=salt)
Defensive patterns
Strategy: validation
Validate before calling
secret = os.environ.get("FASTMCP_JWT_SECRET")
if not secret:
raise RuntimeError("FASTMCP_JWT_SECRET is not set; cannot derive JWT signing key")
derive_jwt_key(high_entropy_material=secret, salt=salt) Type guard
def can_derive_key(high: str | None, low: str | None) -> bool:
return high is not None or low is not None Prevention
- Assert signing-secret env vars exist at application startup
- Verify .env/secret mounts load correctly in every deployment environment
- Generate and store a high-entropy secret in your secrets manager before first deploy
When it happens
Trigger: Calling derive_jwt_key(salt=...) with both material arguments as None (or omitted) — typically when settings/env vars for the signing secret are unset so the caller passes None through.
Common situations: Missing environment variable (e.g. FASTMCP_JWT_SECRET not set in a fresh deployment); secrets manager returned empty on startup and the code passes None silently; a config branch that only fills one material under certain flags; local dev without the .env file loaded.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Either high_entropy_material or low_entropy_material must be
- Could not create the CLI state directory
- OAuth provider has no server URL. Either pass mcp_url to OAu
- OAuth server rejected the static client credentials. Verify
- mode must be 'legacy', 'auto', or one of {list(MODERN_PROTOC
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/0d1f59f1ca6bf5ee.
Report an issue: GitHub.