{"record":{"id":"164f0b072c2a993e","repo":"BerriAI/litellm","slug":"invalid-hash-key-hash-key-hashed-token-decrypt","errorCode":null,"errorMessage":"Invalid hash key. Hash key={hashed_token}. Decrypted token={decrypted_token}. Error: {e}","messagePattern":"Invalid hash key\\. Hash key=(.+?)\\. Decrypted token=(.+?)\\. Error: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/auth_checks.py","lineNumber":2997,"sourceCode":"\n    @staticmethod\n    def get_key_object_from_ui_hash_key(\n        hashed_token: str,\n    ) -> UserAPIKeyAuth | None:\n        import json\n\n        from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth\n        from litellm.proxy.common_utils.encrypt_decrypt_utils import (\n            decrypt_value_helper,\n        )\n\n        decrypted_token: Final = decrypt_value_helper(hashed_token, key=\"ui_hash_key\", exception_type=\"debug\")\n        if decrypted_token is None:\n            return None\n        try:\n            return UserAPIKeyAuth.model_validate(json.loads(decrypted_token))\n        except Exception as e:\n            raise Exception(f\"Invalid hash key. Hash key={hashed_token}. Decrypted token={decrypted_token}. Error: {e}\")\n\n\nasync def _fetch_key_object_from_db_with_reconnect(\n    hashed_token: str,\n    prisma_client: PrismaClient,\n    parent_otel_span: Span | None,\n    proxy_logging_obj: ProxyLogging | None,\n) -> BaseModel | None:\n    \"\"\"\n    Fetch key object from DB and retry once if a DB connection error can be healed.\n    \"\"\"\n    try:\n        return await prisma_client.get_data(\n            token=hashed_token,\n            table_name=\"combined_view\",\n            parent_otel_span=parent_otel_span,\n            proxy_logging_obj=proxy_logging_obj,\n        )","sourceCodeStart":2979,"sourceCodeEnd":3015,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/auth_checks.py#L2979-L3015","documentation":"Raised when decrypting a UI session hash token: decrypt_value_helper succeeded, but json.loads(...) or UserAPIKeyAuth.model_validate(...) failed on the decrypted payload. The token was encrypted with the proxy's ui_hash_key; if the key changed between sessions or the payload is corrupt, decryption/validation produces garbage and this Exception names the hash key, the decrypted token, and the underlying parse error.","triggerScenarios":"Presenting a UI session token minted under a different LITELLM_SALT_KEY/ui_hash_key than the running proxy uses (e.g. salt key rotated, container recreated without the env var), or a truncated/manually edited token in the Authorization header.","commonSituations":"Rotating or losing LITELLM_SALT_KEY so old UI sessions/cookies can no longer be validated; ephemeral Docker containers without a pinned salt key, invalidating sessions on each recreate; tokens persisted from a previous proxy version with a changed UserAPIKeyAuth schema.","solutions":["Re-authenticate through the UI/login flow to mint a fresh token under the current key — stale sessions are the most common cause","Pin LITELLM_SALT_KEY (and any UI session encryption env) to a stable value across restarts/deployments so existing sessions remain valid","If sessions must be invalidated wholesale after a key rotation, clear browser cookies/API clients of the old token"],"exampleFix":"# before (docker-compose, no pinned salt)\nservices:\n  litellm:\n    image: ghcr.io/berriai/litellm\n# after\nservices:\n  litellm:\n    image: ghcr.io/berriai/litellm\n    environment:\n      LITELLM_SALT_KEY: \"${LITELLM_SALT_KEY}\"   # stable across restarts","handlingStrategy":"try-catch","validationCode":"# before sending a stored UI token, sanity-check it still decrypts under this proxy\nfrom litellm.proxy.auth.auth_checks import _get_ui_token_if_valid  # conceptual\nassert token.startswith(\"sk-\") or len(token) > 40, \"stale/corrupt session token\"","typeGuard":null,"tryCatchPattern":"try:\n    user_auth = decode_hashed_token(hashed_token)\nexcept Exception as e:\n    if \"Invalid hash key\" in str(e):\n        # session minted under a different LITELLM_SALT_KEY: force re-login\n        return RedirectResponse(\"/experimental/login/login\")\n    raise","preventionTips":["Pin LITELLM_SALT_KEY to a stable secret across container restarts and deploys","Treat salt-key rotation as a session invalidating event: clear stored tokens and re-authenticate"],"tags":["litellm","ui","encryption","salt-key","session","jwt"],"backgroundTag":"encryption-key-mismatch","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T10:36:37.832Z"}