BerriAI/litellm · error · HTTPException

SAML assertion is missing the required ID attribute.

Error message

SAML assertion is missing the required ID attribute.

What it means

After processing, auth.get_last_assertion_id() returns None: the assertion carries no ID attribute, which the replay guard requires to track consumption. The response is rejected with 401 because replay protection is impossible without an assertion id.

Source

Thrown at litellm/proxy/management_endpoints/sso/saml_sso.py:400

                detail="SAML response is not bound to this browser's login request.",
            )
        elif not SAMLAuthHandler._bool_env("SAML_ALLOW_UNSOLICITED", False):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Unsolicited (IdP-initiated) SAML responses are disabled.",
            )
        elif cache.redis_cache is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=(
                    "Unsolicited (IdP-initiated) SAML responses require a shared Redis cache "
                    "so the replay guard is enforced across every worker."
                ),
            )

        assertion_id: Final = cast(str | None, auth.get_last_assertion_id())  # cast-ok: untyped python3-saml
        if assertion_id is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="SAML assertion is missing the required ID attribute.",
            )
        consumed_key: Final = f"{_SAML_CONSUMED_ASSERTION_CACHE_PREFIX}:{assertion_id}"
        consumed_count: Final = await cache.async_increment_cache(
            key=consumed_key, value=1, ttl=SAMLAuthHandler._replay_guard_ttl(auth)
        )
        if consumed_count is not None and consumed_count > 1:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="SAML assertion has already been used (replay detected).",
            )

    @staticmethod
    def _result_from_auth(auth: "OneLogin_Saml2_Auth") -> CustomOpenID:
        attributes: Final = cast(dict[str, list[str]], auth.get_attributes())  # cast-ok: untyped python3-saml
        name_id: Final = cast(str | None, auth.get_nameid())  # cast-ok: untyped python3-saml

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Configure the IdP to include an ID attribute in the assertion.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at litellm/proxy/management_endpoints/sso/saml_sso.py:400 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/3a9678739d99dd16. Report an issue: GitHub.