{"record":{"id":"4e73e8666024e8f6","repo":"PrefectHQ/fastmcp","slug":"either-high-entropy-material-or-low-entropy-materi","errorCode":null,"errorMessage":"Either high_entropy_material or low_entropy_material must be provided, but not both","messagePattern":"Either high_entropy_material or low_entropy_material must be provided, but not both","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/jwt_issuer.py","lineNumber":47,"sourceCode":"@overload\ndef derive_jwt_key(*, high_entropy_material: str, salt: str) -> bytes:\n    \"\"\"Derive JWT signing key from a high-entropy key material and server salt.\"\"\"\n\n\n@overload\ndef derive_jwt_key(*, low_entropy_material: str, salt: str) -> bytes:\n    \"\"\"Derive JWT signing key from a low-entropy key material and server salt.\"\"\"\n\n\ndef derive_jwt_key(\n    *,\n    high_entropy_material: str | None = None,\n    low_entropy_material: str | None = None,\n    salt: str,\n) -> bytes:\n    \"\"\"Derive JWT signing key from a high-entropy or low-entropy key material and server salt.\"\"\"\n    if high_entropy_material is not None and low_entropy_material is not None:\n        raise ValueError(\n            \"Either high_entropy_material or low_entropy_material must be provided, but not both\"\n        )\n\n    if high_entropy_material is not None:\n        derived_key = HKDF(\n            algorithm=hashes.SHA256(),\n            length=32,\n            salt=salt.encode(),\n            info=b\"Fernet\",\n        ).derive(key_material=high_entropy_material.encode())\n\n        return base64.urlsafe_b64encode(derived_key)\n\n    if low_entropy_material is not None:\n        iterations = (\n            KDF_ITERATIONS_TEST if fastmcp.settings.test_mode else KDF_ITERATIONS\n        )\n        pbkdf2 = PBKDF2HMAC(","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/jwt_issuer.py#L29-L65","documentation":"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.","triggerScenarios":"Calling derive_jwt_key(salt=...) with both high_entropy_material and low_entropy_material set to non-None values (jwt_issuer.py:47).","commonSituations":"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.","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"],"exampleFix":"// before\nderive_jwt_key(high_entropy_material=high, low_entropy_material=low, salt=salt)\n// after\nif high:\n    derive_jwt_key(high_entropy_material=high, salt=salt)\nelse:\n    derive_jwt_key(low_entropy_material=low, salt=salt)","handlingStrategy":"validation","validationCode":"if key_material.high is not None and key_material.low is not None:\n    raise ValueError(\"Configure exactly one of high- or low-entropy signing material\")\nderive_jwt_key(\n    high_entropy_material=key_material.high,\n    low_entropy_material=key_material.low,\n    salt=salt,\n)","typeGuard":"def has_exactly_one_material(high: str | None, low: str | None) -> bool:\n    return (high is None) != (low is None)","tryCatchPattern":null,"preventionTips":["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"],"tags":["configuration","cryptography","jwt-signing"],"backgroundTag":"conflicting-configuration","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}