{"record":{"id":"dfedf43935cb0d7d","repo":"BerriAI/litellm","slug":"user-role-is-required-for-experimental-ui-login","errorCode":null,"errorMessage":"User role is required for experimental UI login","messagePattern":"User role is required for experimental UI login","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/auth/auth_checks.py","lineNumber":2887,"sourceCode":"    except Exception as e:\n        verbose_proxy_logger.exception(\"Error looking up organization by alias: %s\", org_alias)\n        raise HTTPException(\n            status_code=500,\n            detail={\"error\": f\"Error looking up organization by alias '{org_alias}': {e}\"},\n        )\n\n\nclass ExperimentalUIJWTToken:\n    @staticmethod\n    def get_experimental_ui_login_jwt_auth_token(user_info: LiteLLM_UserTable) -> str:\n        from datetime import timedelta\n\n        from litellm.proxy.common_utils.encrypt_decrypt_utils import (\n            encrypt_value_helper,\n        )\n\n        if user_info.user_role is None:\n            raise Exception(\"User role is required for experimental UI login\")\n\n        # Experimental UI flow uses fixed 10-min expiry for security (does not use LITELLM_UI_SESSION_DURATION)\n        expiration_time: Final = get_utc_datetime() + timedelta(minutes=10)\n\n        # Format the expiration time as ISO 8601 string\n        expires: Final = expiration_time.strftime(\"%Y-%m-%dT%H:%M:%S.%f\")[:-3] + \"+00:00\"\n\n        valid_token: Final = UserAPIKeyAuth(\n            token=\"ui-token\",\n            key_name=\"ui-token\",\n            key_alias=\"ui-token\",\n            max_budget=litellm.max_ui_session_budget,\n            rpm_limit=100,  # allow user to have a conversation on test key pane of UI\n            expires=expires,\n            user_id=user_info.user_id,\n            team_id=\"litellm-dashboard\",\n            models=user_info.models,\n            max_parallel_requests=None,","sourceCodeStart":2869,"sourceCodeEnd":2905,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/auth/auth_checks.py#L2869-L2905","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Assign a role to the user: POST /user/update with {\"user_id\": \"...\", \"user_role\": \"internal_user\"} (or proxy_admin for admins)","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","Re-attempt the UI login after the role is saved"],"exampleFix":"curl -X POST http://localhost:4000/user/update \\\n  -H \"Authorization: Bearer $MASTER_KEY\" \\\n  -d '{\"user_id\": \"user-1\", \"user_role\": \"proxy_admin\"}'","handlingStrategy":"validation","validationCode":"user = (await client.get(\"/user/info\", params={\"user_id\": uid})).json()\nassert user.get(\"user_role\") is not None, \"assign a role before UI login\"","typeGuard":"def user_has_role(user: LiteLLM_UserTable) -> bool:\n    return user.user_role is not None","tryCatchPattern":"try:\n    token = ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token(user)\nexcept Exception as e:\n    if \"User role is required\" in str(e):\n        await client.post(\"/user/update\", json={\"user_id\": user.user_id, \"user_role\": \"internal_user\"})\n        user = await reload_user(user.user_id)\n        token = ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token(user)\n    else:\n        raise","preventionTips":["Set default_user_role in SSO/JWT auth config so auto-provisioned users always have a role","Validate user_role on onboarding before allowing UI login"],"tags":["litellm","ui","jwt","user-role","login"],"backgroundTag":"missing-user-role","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}