{"record":{"id":"35d21aa6cf795024","repo":"BerriAI/litellm","slug":"saml-assertion-has-already-been-used-replay-detec","errorCode":null,"errorMessage":"SAML assertion has already been used (replay detected).","messagePattern":"SAML assertion has already been used \\(replay detected\\)\\.","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"litellm/proxy/management_endpoints/sso/saml_sso.py","lineNumber":409,"sourceCode":"                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=(\n                    \"Unsolicited (IdP-initiated) SAML responses require a shared Redis cache \"\n                    \"so the replay guard is enforced across every worker.\"\n                ),\n            )\n\n        assertion_id: Final = cast(str | None, auth.get_last_assertion_id())  # cast-ok: untyped python3-saml\n        if assertion_id is None:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=\"SAML assertion is missing the required ID attribute.\",\n            )\n        consumed_key: Final = f\"{_SAML_CONSUMED_ASSERTION_CACHE_PREFIX}:{assertion_id}\"\n        consumed_count: Final = await cache.async_increment_cache(\n            key=consumed_key, value=1, ttl=SAMLAuthHandler._replay_guard_ttl(auth)\n        )\n        if consumed_count is not None and consumed_count > 1:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=\"SAML assertion has already been used (replay detected).\",\n            )\n\n    @staticmethod\n    def _result_from_auth(auth: \"OneLogin_Saml2_Auth\") -> CustomOpenID:\n        attributes: Final = cast(dict[str, list[str]], auth.get_attributes())  # cast-ok: untyped python3-saml\n        name_id: Final = cast(str | None, auth.get_nameid())  # cast-ok: untyped python3-saml\n\n        email = SAMLAuthHandler._attribute_value(attributes, \"SAML_ATTRIBUTE_EMAIL\", _EMAIL_ATTRIBUTE_CANDIDATES)\n        if email is None and name_id is not None and \"@\" in name_id:\n            email = name_id\n\n        if email is None and SAMLAuthHandler._env(\"ALLOWED_EMAIL_DOMAINS\") is not None:\n            raise HTTPException(\n                status_code=status.HTTP_401_UNAUTHORIZED,\n                detail=(\n                    \"SAML assertion did not contain an email address, but ALLOWED_EMAIL_DOMAINS \"","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/sso/saml_sso.py#L391-L427","documentation":"The replay guard increments a consumption counter for the assertion id in the shared cache; if the count is already above 1, the same assertion was submitted before and the login is refused with 401 as a replay attempt.","triggerScenarios":"Thrown at litellm/proxy/management_endpoints/sso/saml_sso.py:409 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Restart the login flow; the assertion was replayed. Investigate potential replay attacks if persistent."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}