BerriAI/litellm · error · HTTPException

User={key.user_id} is not a member of the team={team.team_id

Error message

User={key.user_id} is not a member of the team={team.team_id}. Check team members via `/team/info`.

What it means

For team-scoped keys, LiteLLM validates that the key's user_id refers to an actual member of that team (looked up via _get_user_in_team against the team's membership table/cached object). If the user is not a member, the key create/update is rejected with 403 and a pointer to /team/info. This keeps spend attribution correct: key spend rolls up to a user who must belong to the team.

Source

Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:3326

    # Check if the key's tpm/rpm limit is less than the team's tpm/rpm limit
    if key.tpm_limit is not None:
        if team.tpm_limit and key.tpm_limit > team.tpm_limit:
            raise HTTPException(
                status_code=403,
                detail=f"Key={key.token} has a tpm_limit={key.tpm_limit} which is greater than the team's tpm_limit={team.tpm_limit}.",
            )
        if team.rpm_limit and key.rpm_limit and key.rpm_limit > team.rpm_limit:
            raise HTTPException(
                status_code=403,
                detail=f"Key={key.token} has a rpm_limit={key.rpm_limit} which is greater than the team's rpm_limit={team.rpm_limit}.",
            )

    # Check if the key's user_id is a member of the team
    member_object: Final = _get_user_in_team(team_table=cast(LiteLLM_TeamTableCachedObj, team), user_id=key.user_id)
    if key.user_id is not None:
        if not member_object:
            raise HTTPException(
                status_code=403,
                detail=f"User={key.user_id} is not a member of the team={team.team_id}. Check team members via `/team/info`.",
            )

    # Check if the person initiating the change is a Proxy Admin or Team Admin
    if (
        change_initiated_by.user_role == LitellmUserRoles.PROXY_ADMIN.value
        or _is_user_team_admin(
            user_api_key_dict=change_initiated_by,
            team_obj=team,
        )
        or TeamMemberPermissionChecks.does_team_member_have_permissions_for_endpoint(
            team_member_object=member_object,
            team_table=cast(LiteLLM_TeamTableCachedObj, team),
            route=KeyManagementRoutes.KEY_UPDATE.value,
        )
    ):
        return

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Add the user to the team first: POST /team/member_add with the team_id and the user, then retry the key operation.
  2. Verify membership with GET /team/info and check the members list for the exact user_id.
  3. Or drop user_id from the key request if the key need not be bound to a specific user.

Example fix

# before
client.post("/key/generate", json={"team_id": tid, "user_id": uid, "models": [...]})

# after
client.post("/team/member_add", json={"team_id": tid, "member": {"user_id": uid, "role": "user"}})
client.post("/key/generate", json={"team_id": tid, "user_id": uid, "models": [...]})
Defensive patterns

Strategy: validation

Validate before calling

def ensure_user_in_team(client, team_id: str, user_id: str) -> None:
    info = client.get("/team/info", params={"team_id": team_id}).json()
    members = info.get("team_info", {}).get("members", []) or info.get("members", [])
    if user_id not in {m.get("user_id") for m in members}:
        client.post("/team/member_add", json={"team_id": team_id, "member": {"user_id": user_id, "role": "user"}})

Try / catch

try:
    client.post("/key/generate", json={"team_id": tid, "user_id": uid, ...})
except HTTPError as e:
    if e.response.status_code == 403 and "not a member of the team" in e.response.text:
        client.post("/team/member_add", json={"team_id": tid, "member": {"user_id": uid, "role": "user"}})
        retry_create_key(tid, uid)
    else:
        raise

Prevention

When it happens

Trigger: POST /key/generate or /key/update with team_id X and user_id of a user who has never been added to team X (or was removed from it); creating a key for a user by email when the membership record uses a different user_id.

Common situations: New employee provisioned a key before the team-member add completed; user was removed from the team during offboarding but automation still refreshes their key; user_id mismatch between your IdP and LiteLLM's user table.

Related errors


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