{"record":{"id":"0d1f59f1ca6bf5ee","repo":"PrefectHQ/fastmcp","slug":"either-high-entropy-material-or-low-entropy-materi-0d1f59","errorCode":null,"errorMessage":"Either high_entropy_material or low_entropy_material must be provided","messagePattern":"Either high_entropy_material or low_entropy_material must be provided","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/jwt_issuer.py","lineNumber":74,"sourceCode":"            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(\n            algorithm=hashes.SHA256(),\n            length=32,\n            salt=salt.encode(),\n            iterations=iterations,\n        ).derive(key_material=low_entropy_material.encode())\n\n        return base64.urlsafe_b64encode(pbkdf2)\n\n    raise ValueError(\n        \"Either high_entropy_material or low_entropy_material must be provided\"\n    )\n\n\nclass JWTIssuer:\n    \"\"\"Issues and validates FastMCP-signed JWT tokens using HS256.\n\n    This issuer creates JWT tokens for MCP clients with proper audience claims,\n    maintaining OAuth 2.0 token boundaries. Tokens are signed with HS256 using\n    a key derived from the upstream client secret.\n    \"\"\"\n\n    def __init__(\n        self,\n        issuer: str,\n        audience: str,\n        signing_key: bytes,\n    ):","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/jwt_issuer.py#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nderive_jwt_key(salt=salt)  # no material configured\n// after\nsecret = os.environ[\"FASTMCP_JWT_SECRET\"]\nderive_jwt_key(high_entropy_material=secret, salt=salt)","handlingStrategy":"validation","validationCode":"secret = os.environ.get(\"FASTMCP_JWT_SECRET\")\nif not secret:\n    raise RuntimeError(\"FASTMCP_JWT_SECRET is not set; cannot derive JWT signing key\")\nderive_jwt_key(high_entropy_material=secret, salt=salt)","typeGuard":"def can_derive_key(high: str | None, low: str | None) -> bool:\n    return high is not None or low is not None","tryCatchPattern":null,"preventionTips":["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"],"tags":["configuration","missing-env-var","jwt-signing","cryptography"],"backgroundTag":"missing-env-var","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}