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, but not both
What it means
derive_jwt_key() accepts exactly one kind of key material: high-entropy material (used directly via HKDF) or low-entropy material (run through PBKDF2 with iterations). Passing both is ambiguous — the function cannot know which derivation the server that will verify the token expects — so it raises ValueError immediately.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/jwt_issuer.py:47
@overload
def derive_jwt_key(*, high_entropy_material: str, salt: str) -> bytes:
"""Derive JWT signing key from a high-entropy key material and server salt."""
@overload
def derive_jwt_key(*, low_entropy_material: str, salt: str) -> bytes:
"""Derive JWT signing key from a low-entropy key material and server salt."""
def derive_jwt_key(
*,
high_entropy_material: str | None = None,
low_entropy_material: str | None = None,
salt: str,
) -> bytes:
"""Derive JWT signing key from a high-entropy or low-entropy key material and server salt."""
if high_entropy_material is not None and low_entropy_material is not None:
raise ValueError(
"Either high_entropy_material or low_entropy_material must be provided, but not both"
)
if high_entropy_material is not None:
derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
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(View on GitHub (pinned to 1f02114297)
Solutions
- Remove one of the two arguments so only high_entropy_material or only low_entropy_material is passed
- If migrating from low- to high-entropy material, gate with an if/else choosing exactly one based on which secret exists
- Validate your settings object before constructing the issuer and fail fast with a clear config error
- Ensure only the matching derivation is used on all server replicas — verifier and issuer must agree
Example fix
// before
derive_jwt_key(high_entropy_material=high, low_entropy_material=low, salt=salt)
// after
if high:
derive_jwt_key(high_entropy_material=high, salt=salt)
else:
derive_jwt_key(low_entropy_material=low, salt=salt) Defensive patterns
Strategy: validation
Validate before calling
if key_material.high is not None and key_material.low is not None:
raise ValueError("Configure exactly one of high- or low-entropy signing material")
derive_jwt_key(
high_entropy_material=key_material.high,
low_entropy_material=key_material.low,
salt=salt,
) Type guard
def has_exactly_one_material(high: str | None, low: str | None) -> bool:
return (high is None) != (low is None) Prevention
- Model key material as a single mutually-exclusive field in settings
- Fail fast at config-load time when both secrets are present
- Document migration paths so old and new secrets are never both active
When it happens
Trigger: Calling derive_jwt_key(salt=...) with both high_entropy_material and low_entropy_material set to non-None values (jwt_issuer.py:47).
Common situations: Config loaders populate both settings from env vars and pass everything through; a refactor added low_entropy_material without removing the old high_entropy_material argument; secrets management injects both a legacy secret and a new generated key.
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/4e73e8666024e8f6.
Report an issue: GitHub.