BerriAI/litellm · error · Exception

User role is required for experimental UI login

Error message

User role is required for experimental UI login

What it means

Raised by ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token() when the user row being logged in has user_role set to NULL. The experimental UI login flow embeds the role (proxy_admin, internal_user, etc.) into a short-lived (10-minute) encrypted JWT session token; without a role the UI cannot do role-based routing, so token generation is refused.

Source

Thrown at litellm/proxy/auth/auth_checks.py:2887

    except Exception as e:
        verbose_proxy_logger.exception("Error looking up organization by alias: %s", org_alias)
        raise HTTPException(
            status_code=500,
            detail={"error": f"Error looking up organization by alias '{org_alias}': {e}"},
        )


class ExperimentalUIJWTToken:
    @staticmethod
    def get_experimental_ui_login_jwt_auth_token(user_info: LiteLLM_UserTable) -> str:
        from datetime import timedelta

        from litellm.proxy.common_utils.encrypt_decrypt_utils import (
            encrypt_value_helper,
        )

        if user_info.user_role is None:
            raise Exception("User role is required for experimental UI login")

        # Experimental UI flow uses fixed 10-min expiry for security (does not use LITELLM_UI_SESSION_DURATION)
        expiration_time: Final = get_utc_datetime() + timedelta(minutes=10)

        # Format the expiration time as ISO 8601 string
        expires: Final = expiration_time.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "+00:00"

        valid_token: Final = UserAPIKeyAuth(
            token="ui-token",
            key_name="ui-token",
            key_alias="ui-token",
            max_budget=litellm.max_ui_session_budget,
            rpm_limit=100,  # allow user to have a conversation on test key pane of UI
            expires=expires,
            user_id=user_info.user_id,
            team_id="litellm-dashboard",
            models=user_info.models,
            max_parallel_requests=None,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Assign a role to the user: POST /user/update with {"user_id": "...", "user_role": "internal_user"} (or proxy_admin for admins)
  2. If users come from SSO/JWT auth, set default_user_role (e.g. 'internal_user') in the auth configuration so auto-provisioned users always get a role
  3. Re-attempt the UI login after the role is saved

Example fix

curl -X POST http://localhost:4000/user/update \
  -H "Authorization: Bearer $MASTER_KEY" \
  -d '{"user_id": "user-1", "user_role": "proxy_admin"}'
Defensive patterns

Strategy: validation

Validate before calling

user = (await client.get("/user/info", params={"user_id": uid})).json()
assert user.get("user_role") is not None, "assign a role before UI login"

Type guard

def user_has_role(user: LiteLLM_UserTable) -> bool:
    return user.user_role is not None

Try / catch

try:
    token = ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token(user)
except Exception as e:
    if "User role is required" in str(e):
        await client.post("/user/update", json={"user_id": user.user_id, "user_role": "internal_user"})
        user = await reload_user(user.user_id)
        token = ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token(user)
    else:
        raise

Prevention

When it happens

Trigger: POST /experimental/login/login (or the equivalent UI auth flow) for a user whose LiteLLM_UserTable row has user_role NULL — e.g. a user auto-created via SSO/JWT custom auth with no default role configured, or a user inserted directly into the DB without a role.

Common situations: SSO/JWT auth provisioning users with no default_user_role set in config; manually inserted user rows; older user records created before roles were introduced.

Related errors


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