litellm - 💡(How to fix) Fix [Bug]: get_key_models() replaces team ACL with proxy-wide list when "all-proxy-models" sentinel is present, breaking /v1/models for team keys [2 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

Root cause is in get_key_models() at litellm/proxy/auth/model_checks.py:114-115. When the team's ACL contains the "all-proxy-models" sentinel, the function replaces the resolved model list with proxy_model_list instead of unioning it. Any team-specific models in the ACL are discarded.

Fix Action

Fixed

Code Example

Step 1: Team created without specifying models (e.g. via Terraform/API)
NewTeamRequest.models defaults to [] (TeamBase, _types.py:1494)
        → exclude_none=True keeps [] (not None)
DB stores models = []

Step 2: Models added later
add_new_models_to_team() sees current_models = []
        → comment says "implies all model access"
        → injects "all-proxy-models" sentinel alongside the named models
DB: models = ["all-proxy-models", "gpt-4o", "team-claude-sonnet", ...]

Step 3: Key with models: ["all-team-models"] calls /v1/models
get_key_models() resolves "all-team-models" → team ACL
ACL contains "all-proxy-models"
        → model_checks.py:114-115 hits replacement branch:
              all_models = proxy_model_list   ← discards team entries
        → every team-specific entry in the ACL is gone

Step 4: get_complete_model_list()
        → key_models is populated (proxy-wide) → uses key_models, skips team branch
/v1/models returns only proxy-wide models

---

# Before (replacement — destroys team-specific entries):
if SpecialModelNames.all_proxy_models.value in all_models:
    all_models = proxy_model_list

# After (union — matches what get_team_models already does correctly at line 142):
if SpecialModelNames.all_proxy_models.value in all_models:
    all_models = list(set(all_models) | set(proxy_model_list))

---

# Before:
models: list = []

# After:
models: Optional[list] = None
RAW_BUFFERClick to expand / collapse

What happened?

GET /v1/models returns only proxy-wide models for keys bound to a team, even when the team's ACL contains team-specific entries. The team's models are silently dropped.

Root cause is in get_key_models() at litellm/proxy/auth/model_checks.py:114-115. When the team's ACL contains the "all-proxy-models" sentinel, the function replaces the resolved model list with proxy_model_list instead of unioning it. Any team-specific models in the ACL are discarded.

The sentinel ends up in the ACL through a separate write-path quirk: a team created without specifying models is stored with models = [], and the first call to add_new_models_to_team injects "all-proxy-models" because it treats an empty list as "all access". This is the path Terraform-provisioned teams take.

get_team_models() at line 142 already handles the same sentinel correctly using set.update() (union). The two read paths disagree on semantics.

Expected: /v1/models returns the union of the team's allowed models and proxy-wide models. Actual: /v1/models returns only proxy-wide models; team-specific entries are dropped.

The admin UI is not affected because it uses get_all_team_and_direct_access_models(), which queries router deployments by team_id and bypasses this code path entirely.

Bug chain

Step 1: Team created without specifying models (e.g. via Terraform/API)
        → NewTeamRequest.models defaults to [] (TeamBase, _types.py:1494)
        → exclude_none=True keeps [] (not None)
        → DB stores models = []

Step 2: Models added later
        → add_new_models_to_team() sees current_models = []
        → comment says "implies all model access"
        → injects "all-proxy-models" sentinel alongside the named models
        → DB: models = ["all-proxy-models", "gpt-4o", "team-claude-sonnet", ...]

Step 3: Key with models: ["all-team-models"] calls /v1/models
        → get_key_models() resolves "all-team-models" → team ACL
        → ACL contains "all-proxy-models"
        → model_checks.py:114-115 hits replacement branch:
              all_models = proxy_model_list   ← discards team entries
        → every team-specific entry in the ACL is gone

Step 4: get_complete_model_list()
        → key_models is populated (proxy-wide) → uses key_models, skips team branch
        → /v1/models returns only proxy-wide models

Inconsistent semantics for models = [] and the sentinel

Locationmodels = [] treatmentBehavior
add_new_models_to_team (write)"all access"Injects "all-proxy-models"
get_team_models (read)"no models"Returns [], cascade falls through
get_key_models "all-proxy-models" branchReplacementDestroys team-specific entries

Affected files

  • litellm/proxy/auth/model_checks.pyget_key_models() lines 114–115: all_models = proxy_model_list replaces instead of unioning.
  • litellm/proxy/_types.pyTeamBase.models line 1494: models: list = [] default causes [] to be written to the DB on team creation.
  • litellm/proxy/management_endpoints/team_endpoints.pyadd_new_models_to_team around lines 3596–3599: injects "all-proxy-models" sentinel when the existing ACL is empty.

Steps to Reproduce

  1. Create a team via the API or Terraform without specifying models in the /team/new request.
  2. Add model deployments to the team via /model/new with model_info.team_id set (or via the Terraform model resource).
  3. Inspect the team in the DB — LiteLLM_TeamTable.models will contain "all-proxy-models" alongside the named models.
  4. Create a key bound to the team with models: ["all-team-models"].
  5. Call GET /v1/models with that key.

Expected: response contains team-specific models. Actual: response contains only proxy-wide models.

Relevant log output

(none — silent behavior; verified by inspecting DB state and response payload)

What part of LiteLLM is this about?

Proxy

LiteLLM version

v1.83.10-stable

Suggested fix

Fix 1 — get_key_models() should union, not replace

litellm/proxy/auth/model_checks.py lines 114–115:

# Before (replacement — destroys team-specific entries):
if SpecialModelNames.all_proxy_models.value in all_models:
    all_models = proxy_model_list

# After (union — matches what get_team_models already does correctly at line 142):
if SpecialModelNames.all_proxy_models.value in all_models:
    all_models = list(set(all_models) | set(proxy_model_list))

Fix 2 — TeamBase.models should default to None

litellm/proxy/_types.py line 1494:

# Before:
models: list = []

# After:
models: Optional[list] = None

This prevents the empty-list-to-sentinel injection path from triggering on first model add. Fix 1 is the critical runtime fix — it corrects behavior even for teams whose ACL already contains "all-proxy-models".

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - 💡(How to fix) Fix [Bug]: get_key_models() replaces team ACL with proxy-wide list when "all-proxy-models" sentinel is present, breaking /v1/models for team keys [2 pull requests]