BerriAI/litellm · warning · HTTPException

User blocked from making LLM API Calls. User={user}

Error message

User blocked from making LLM API Calls. User={user}

What it means

Raised as HTTPException 400 by the BlockedUserList async_pre_call_hook when the request's user/user_id (from the request body) is present in the statically configured blocked_user_list. This is an intentional access-control rejection at the pre-call stage, before any LLM call is made.

Source

Thrown at enterprise/enterprise_hooks/blocked_user_list.py:78

        data: dict,
        call_type: str,
    ):
        try:
            """
            - check if user id part of call
            - check if user id part of blocked list
                - if blocked list is none or user not in blocked list
                - check if end-user in cache
                - check if end-user in db
            """
            self.print_verbose("Inside Blocked User List Pre-Call Hook")
            if "user_id" in data or "user" in data:
                user = data.get("user_id", data.get("user", ""))
                if (
                    self.blocked_user_list is not None
                    and user in self.blocked_user_list
                ):
                    raise HTTPException(
                        status_code=400,
                        detail={
                            "error": f"User blocked from making LLM API Calls. User={user}"
                        },
                    )

                cache_key = f"litellm:end_user_id:{user}"
                end_user_cache_obj: Optional[LiteLLM_EndUserTable] = cache.get_cache(  # type: ignore
                    key=cache_key
                )
                if end_user_cache_obj is None and self.prisma_client is not None:
                    # check db
                    end_user_obj = (
                        await self.prisma_client.db.litellm_endusertable.find_unique(
                            where={"user_id": user}
                        )
                    )
                    if end_user_obj is None:  # user not in db - assume not blocked

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. The request is blocked by policy — the end user must be unblocked by the admin (remove their ID from blocked_user_list) or the request must use a different, permitted user identifier.
  2. Admins: verify the entry in blocked_user_list matches exactly (no whitespace/newline artifacts from the file — trailing newlines create empty or mismatched entries).
  3. Client side: catch the 400 and stop retrying — retries will not succeed while blocked.
Defensive patterns

Strategy: try-catch

Validate before calling

blocked = set(load_blocked_users())
user_id = payload.get("user_id") or payload.get("user")
if user_id in blocked:
    raise UserBlockedError(user_id)  # fail before spending a round trip

Try / catch

try:
    resp = client.chat.completions.create(..., user=user_id)
except HTTPException as e:
    if e.status_code == 400 and "User blocked" in str(e.detail):
        mark_user_blocked_locally(user_id)  # stop sending for this user
    raise

Prevention

When it happens

Trigger: A /chat/completions (or similar) request whose body contains "user" or "user_id" equal to an entry in blocked_users.txt / the configured list. The check is an exact membership test on data.get("user_id", data.get("user", "")).

Common situations: A deactivated user or flagged end-user still has valid application credentials and keeps sending traffic; blocklist updated to include the user and their next request is rejected; test scripts hardcoding a now-blocked user ID.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/96615c63a64ad430. Report an issue: GitHub.