Skip to content

[Bug]: Org-scoped teams with empty model list bypass organization model restrictions #17283

@rioiart

Description

@rioiart

What happened?

When creating a team under an organization that has restricted models, leaving the model list empty allows the team to have "all proxy models" access, bypassing the organization's model restrictions.

Current Behavior

  1. Organization is created with restricted models: ["gpt-4", "gpt-3.5-turbo"]
  2. Team is created under that organization with models: [] (empty list) or models not specified
  3. Team is created successfully with no validation error
  4. The team effectively has access to all proxy models (empty list = all models)
  5. This exceeds the parent organization's model restrictions

Expected Behavior

When creating/updating a team under an organization with restricted models:

  1. Empty or unspecified model list should be rejected with an error message indicating that models must be explicitly specified
  2. all-proxy-models should be rejected since it would give access beyond the org's restrictions
  3. A new all-org-models special value should be supported to allow teams to inherit the organization's model list (similar to how all-team-models works for keys)

This is consistent with how keys work under teams - the UI doesn't allow an empty model list, users must select "All Team Models" or specific models.

Root Cause

In _check_org_team_limits() (litellm/proxy/management_endpoints/team_endpoints.py), the model validation logic is:

if data.models is not None and len(org_table.models) > 0:
    for m in data.models:
        if m not in org_table.models:
            raise HTTPException(...)

This validation:

  • Skips entirely if data.models is None
  • Iterates 0 times if data.models is [] (empty list)
  • Only validates explicit models against the org's allowed list
  • No explicit check for all-proxy-models: If an organization has ["all-proxy-models"] in its model list, a team requesting all-proxy-models would pass validation (since the string is in the org's list). However, all-proxy-models should always be rejected for org-scoped teams because it creates a direct binding to all proxy models rather than inheriting from the organization. If the org's model list is later changed to be more restrictive, the team would still have access to all models.

The correct validation exists in validate_team_org_change() (used for team updates changing org), but is not applied during team creation.

Steps to Reproduce

  1. Create an organization with restricted models:

    curl -X POST 'http://localhost:4000/organization/new' \
      -H 'Authorization: Bearer sk-...' \
      -H 'Content-Type: application/json' \
      -d '{"organization_alias": "test-org", "models": ["gpt-4", "gpt-3.5-turbo"]}'
  2. Create a team under that organization with empty models:

    curl -X POST 'http://localhost:4000/team/new' \
      -H 'Authorization: Bearer sk-...' \
      -H 'Content-Type: application/json' \
      -d '{"team_alias": "test-team", "organization_id": "<org-id>", "models": []}'
  3. Observe: Team is created successfully (should fail with validation error)

Proposed Fix

In _check_org_team_limits(), add validation to reject empty/unspecified models when org has restrictions:

if len(org_table.models) > 0:
    # Check for empty/unspecified models
    if data.models is None or len(data.models) == 0:
        raise HTTPException(
            status_code=400,
            detail={
                "error": f"Team must specify models when organization has restricted models. Use 'all-org-models' to inherit organization's models, or select specific models from: {org_table.models}"
            },
        )

    # Check for all-proxy-models (not allowed for org-scoped teams)
    if SpecialModelNames.all_proxy_models.value in data.models:
        raise HTTPException(
            status_code=400,
            detail={
                "error": "Cannot use 'all-proxy-models' for org-scoped teams. Use 'all-org-models' to inherit organization's models."
            },
        )

    # Allow all-org-models special value
    if "all-org-models" not in data.models:
        # Validate explicit models against org's allowed list
        for m in data.models:
            if m not in org_table.models:
                raise HTTPException(...)

Additionally:

  1. Add all_org_models = "all-org-models" to SpecialModelNames enum
  2. Add runtime resolution of all-org-models to org's models in model_checks.py

Relevant log output

Are you a ML Ops Team?

No

What LiteLLM version are you on ?

4fffd33

Twitter / LinkedIn details

https://www.linkedin.com/in/rioiart/

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions