Skip to content

fix(proxy): hide model allowlist from client-facing model access denied errors - #41310

Merged
yassin-berriai merged 12 commits into
mainfrom
litellm_model_access_denied_message
Sep 16, 2026
Merged

yassin-berriai merged 12 commits into
mainfrom
litellm_model_access_denied_message

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Model access denials leak access group names and allowed-model lists to clients
  • JWT role and scope denials leak the role's allowed-model list
  • MCP sampling denials forward the allowlist to the MCP server

How it solves it:

  • Clients now get one fixed message naming only the requested model
  • The detailed denial reason goes to the proxy log instead, once per denial
  • Status codes, error types and JWT response shapes are unchanged

User Flow

Before: a developer with a restricted key sees the proxy's internal access groups in the error

  1. The developer sends POST http://localhost:4000/v1/chat/completions with "model": "anthropic-sonnet-4-5" and a key that is only allowed the internal-models access group
  2. They get HTTP 403 with key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5
  3. A JWT user outside the role gets HTTP 403 with Role=internal_user not allowed to call model=anthropic-sonnet-4-5. Allowed models=['anthropic-haiku-4-5']
  4. An MCP server that asks the proxy to sample from a model the key cannot use gets the same allowlist text back in the tool error

After: the client sees a plain message and the internal detail stays in the proxy log

  1. The developer sends the same POST http://localhost:4000/v1/chat/completions with "model": "anthropic-sonnet-4-5" and the restricted key
  2. They get HTTP 403 with The requested model 'anthropic-sonnet-4-5' is not available for this API key, or the model name is invalid. Check the models available to you and try again., same key_model_access_denied error type as before
  3. The JWT user gets HTTP 403 with the same sentence and the same auth_error type
  4. The MCP server gets the same sentence as the tool error, nothing else
  5. The proxy log has key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5 (or the role or MCP equivalent) for each denial that reached the client

Relevant issues

Affected release

Linear ticket

Resolves LIT-5283

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Shared setup: real Postgres, real Anthropic API, proxy started with PYTHONPATH=<checkout> python -m litellm.proxy.proxy_cli --config config.yaml --port 4000. config.yaml has anthropic-haiku-4-5 in access group internal-models and anthropic-sonnet-4-5 in access group general-models; $K is a key generated with "models": ["internal-models"] and $G a key generated with "models": ["sonnet-group"], a DB access group containing anthropic-sonnet-4-5. The JWT cases use config_jwt.yaml (base on port 4011, head on port 4010) with enable_jwt_auth, a local JWKS server, jwt_role_based_models: internal_user: [anthropic-haiku-4-5], and $JWT minted for roles: [internal_user]. The MCP sampling cases use config_mcp.yaml (base on port 4017, head on port 4016) with a local MCP server on http://127.0.0.1:8765/mcp registered as sampling_demo with allow_sampling: true, whose ask_model tool asks the proxy to sample from the model it is given; $M is a key generated with "models": ["internal-models"] and access to that server, called through POST /mcp-rest/tools/call. Same fixtures on both sides, both trees started with PYTHONPATH pointing at their own checkout

Before (1b8daf2, the merge base before origin/main was merged in; the merged commits do not touch the denial, JWT or MCP sampling paths)

Control: restricted key calls a model it is allowed

  1. curl -s -w "HTTP %{http_code}\n" http://localhost:4000/v1/chat/completions -H "Authorization: Bearer $K" -H "Content-Type: application/json" -d '{"model":"anthropic-haiku-4-5","messages":[{"role":"user","content":"Reply with the single word ok"}]}'
  2. HTTP 200, {"id": "chatcmpl-6fa470f7-3a02-4f31-a26f-c7395535be21", "model": "anthropic-haiku-4-5", "content": "ok"}

Restricted key calls a model outside its access group

  1. curl -s -w "\nHTTP %{http_code}\n" http://localhost:4000/v1/chat/completions -H "Authorization: Bearer $K" -H "Content-Type: application/json" -d '{"model":"anthropic-sonnet-4-5","messages":[{"role":"user","content":"hi"}]}'
  2. {"error":{"message":"key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5","type":"key_model_access_denied","param":"model","code":"403"}} HTTP 403

Key granted through a DB access group calls the model

  1. curl -s -w "HTTP %{http_code}\n" http://localhost:4000/v1/chat/completions -H "Authorization: Bearer $G" -H "Content-Type: application/json" -d '{"model":"anthropic-sonnet-4-5","messages":[{"role":"user","content":"Reply with the single word ok"}],"max_tokens":5}'
  2. HTTP 200 with "content": "ok"

Denied model name carries CR/LF

  1. curl -s -w "\nHTTP %{http_code}\n" http://localhost:4000/v1/chat/completions -H "Authorization: Bearer $K" -H "Content-Type: application/json" -d '{"model":"gpt-x\r\nWARNING forged log line","messages":[{"role":"user","content":"hi"}]}'
  2. HTTP 403 with the allowlist text ending in Tried to access gpt-x\r\nWARNING forged log line

JWT role calls a model outside its role

  1. curl -s -w "\nHTTP %{http_code}\n" http://localhost:4011/v1/chat/completions -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" -d '{"model":"anthropic-haiku-4-5","messages":[{"role":"user","content":"Reply with the single word ok"}]}' returns HTTP 200 with "content": "ok" (control)
  2. Same curl with "model":"anthropic-sonnet-4-5": {"error":{"message":"Role=internal_user not allowed to call model=anthropic-sonnet-4-5. Allowed models=['anthropic-haiku-4-5']","type":"auth_error","param":"None","code":"403"}} HTTP 403

MCP sampling from a restricted key for a model outside its access group

  1. Control: curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:4017/mcp-rest/tools/call -H "Authorization: Bearer $M" -H "Content-Type: application/json" -d '{"server_id":"<server_id>","name":"ask_model","arguments":{"question":"Reply with the single word ok","model":"anthropic-haiku-4-5"}}' returns HTTP 200 with "text":"model=claude-haiku-4-5-20251001 text=ok" and "isError":false (real Anthropic call through MCP sampling)
  2. Same curl with "model":"anthropic-sonnet-4-5": HTTP 200 with "isError":true and "text":"Error executing tool ask_model: Model access denied: the API key is not authorized to use model 'anthropic-sonnet-4-5'. key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5", so the allowlist reaches the MCP server and its user

After (b085a3c)

Control: restricted key calls a model it is allowed

  1. Same curl as Before
  2. HTTP 200, {"id": "chatcmpl-eb731f2d-5cf8-4b19-b546-e148e19e51d2", "model": "anthropic-haiku-4-5", "content": "ok"}

Restricted key calls a model outside its access group

  1. Same curl as Before
  2. {"error":{"message":"The requested model 'anthropic-sonnet-4-5' is not available for this API key, or the model name is invalid. Check the models available to you and try again.","type":"key_model_access_denied","param":"model","code":"403"}} HTTP 403
  3. The proxy log gains exactly one line: LiteLLM Proxy:WARNING: proxy_server.py:1687 - key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5

Key granted through a DB access group calls the model

  1. Same curl as Before
  2. HTTP 200, {"id": "chatcmpl-201de679-1ca7-434a-9882-f4a22bbcaa2f", "model": "anthropic-sonnet-4-5", "content": "ok"}
  3. rg -c "not allowed to access model" proxy.log since the request: 0. The native key check fails and the access group check then passes, and that recovered denial is no longer logged

Denied model name carries CR/LF

  1. Same curl as Before
  2. {"error":{"message":"The requested model 'gpt-x\r\nWARNING forged log line' is not available for this API key, or the model name is invalid. Check the models available to you and try again.","type":"key_model_access_denied","param":"model","code":"403"}} HTTP 403, the model string is untouched in the response
  3. The proxy log has a single denial line key not allowed to access model. This key can only access models=['internal-models']. Tried to access gpt-xWARNING forged log line, CR and LF removed before logging so the value cannot start a new log record

JWT role calls a model outside its role

  1. Control on port 4010, same curl as Before: HTTP 200, {"id": "chatcmpl-dc268e4b-0759-418d-ae69-0a06628c2bbe", "model": "anthropic-haiku-4-5", "content": "ok"}
  2. Same denied curl as Before: {"error":{"message":"The requested model 'anthropic-sonnet-4-5' is not available for this API key, or the model name is invalid. Check the models available to you and try again.","type":"auth_error","param":"None","code":"403"}} HTTP 403
  3. The proxy log has LiteLLM Proxy:WARNING: proxy_server.py:1687 - Role=internal_user not allowed to call model=anthropic-sonnet-4-5. Allowed models=['anthropic-haiku-4-5']

MCP sampling from a restricted key for a model outside its access group

  1. Control on port 4016, same curl as Before: HTTP 200 with "text":"model=claude-haiku-4-5-20251001 text=ok" and "isError":false
  2. Same denied curl as Before: HTTP 200 with "isError":true and "text":"Error executing tool ask_model: The requested model 'anthropic-sonnet-4-5' is not available for this API key, or the model name is invalid. Check the models available to you and try again.", no allowlist in the MCP result
  3. The proxy log has LiteLLM:WARNING: sampling_handler.py:889 - MCP sampling: model access denied for model=anthropic-sonnet-4-5: key not allowed to access model. This key can only access models=['internal-models']. Tried to access anthropic-sonnet-4-5

Type

🐛 Bug Fix

Caveats (if any)

Medium

  • Restricted keys get the same text for unknown and denied models, so they cannot tell them apart
    • Unrestricted keys asking for an unknown model still get the existing 400 Invalid model name text
    • Only the 403 access-denial paths (key, team, user, org, project, team member, JWT role, JWT scope) use the new text

Low

  • The wording is fixed. An earlier revision of this PR made it configurable and editable in the Admin UI; that was dropped as overkill
  • The internal denial reason travels on the raised exception (ModelAccessDeniedProxyException, ModelAccessDeniedHTTPException) and is logged once where the denial leaves the proxy: the ProxyException handler for HTTP routes, the realtime websocket rejection, and the MCP sampling access check that turns it into an MCP error result. Access group fallbacks and probes that catch the denial and then allow the request do not log anything. CR and LF are stripped from the internal reason before logging; the client-facing text keeps the model string as sent
  • Model access denials raised outside the request path (/key/generate and /key/update model checks against the team, /team/update moving a team into an organization, /organization/new, the batch input file model check, vector store routing hints) return the fixed text but are not logged with the internal reason. They were not logged before this PR either
  • JWT role and scope denials keep their HTTPException shape (auth_error, and {"error": ...} for scopes) and only swap the message text
  • expose_router_debug_in_errors is untouched; fallback error wording is out of scope for this PR

Taxonomy audit (D, P, Z, T, H, C, M): D2/Z7 not applicable, the message is built after the existing checks have already decided, alias and access-group resolution is untouched. D4/Z2 not applicable, no new lookup, nothing on the path can widen access. P4: restricted keys can no longer distinguish unknown from denied models, which is the requested behavior. C1: no default flips. C2: no status code or error type changes, verified above. C3: no public symbols removed, ProxyException and HTTPException catchers still catch the subclasses. M/N: the MCP sampling result now carries only the fixed text; the detailed reason is logged on the proxy, never returned to the upstream server. T1: every new test fails on the unfixed code (mutations tried: put the internal reason back in the public message, drop the handler log line, drop the CR/LF strip, route the JWT role check and the JWT scope check around the helper, return internal_message from the MCP sampling check; each made at least one test fail, all restored and verified with cmp -s). T17: no leaked global state. H1/H2: no docs or comments added. H3: no em dashes in copy. H4/H5: str | None, no Any. H6: the message constant lives in the leaf helper module with the exception class that uses it, no litellm.proxy.* import in that module. H8: no new Pydantic fields, no schema change, no dashboard change

Live base-vs-head sweep on 1b8daf20e0 vs this head: the raise sites in auth_checks.py and handle_jwt.py, the ProxyException handler, the auth exception handler conversion, and the MCP sampling access check were driven through /v1/chat/completions (key allowlist, DB access group, CR/LF) on port 4000, JWT role on port 4010, and MCP sampling through a real upstream MCP server on port 4016, all against real Anthropic. The realtime rejection was not driven live (no realtime model in the rig) and is covered by tests/test_litellm/proxy/test_proxy_server.py, which fails when the log call is removed. Not driven live: /v1/responses, /v1/messages and the other LLM routes that share _can_object_call_model, which reach the same raise sites through the same auth call and are covered by the unit tests, and the management, batch and vector store callers listed under Low, whose only change is the exception subclass


Note

Medium Risk
Changes user-visible 403 error text across auth, JWT, realtime, and MCP paths without altering authorization logic; operators and clients that parsed old denial messages may need to adjust.

Overview
Stops leaking allowlists and policy details when the proxy denies model access. Clients (HTTP, JWT, realtime close reasons, MCP sampling) now get one fixed message that only names the requested model; the previous verbose text (allowed models, access groups, roles, team member lists) is kept in internal_message and written to proxy logs once at the boundary.

Adds ModelAccessDeniedProxyException / ModelAccessDeniedHTTPException and model_access_denied_client_message(), and switches key/team/user/member checks in auth_checks.py and JWT role/scope checks in handle_jwt.py to raise them. The ProxyException handler and realtime model-access rejection log sanitized internal reasons (CR/LF stripped); MCP _check_model_access returns the generic message for the new exception type. 403 types and shapes are unchanged; tests assert the new client copy and that allowlists stay out of responses.

Reviewed by Cursor Bugbot for commit b085a3c. Bugbot is set up for automated code reviews on this repo. Configure here.

Add litellm_settings.model_access_denied_message, a template ({model} placeholder) returned to clients instead of the detailed "can only access models=[...]" text on key/team/user/org/project and team-member model access denials. The full denial reason is still written to the proxy logs at WARNING. Unset keeps the existing detailed message, status codes and error types are unchanged.

Expose the new setting and the existing expose_router_debug_in_errors flag in the Admin UI general settings (String editor, Boolean toggle with an explicit True default) and allow both as safe DB overrides so they persist and propagate across workers.

Resolves LIT-5283

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed

codspeed Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_model_access_denied_message (e48dde7) with main (e941edd)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR hardens model-access denial handling by separating fixed client-facing messages from detailed internal diagnostics.

  • Introduces specialized proxy and HTTP exceptions carrying an internal denial reason.
  • Applies the generic response to key, team, user, project, organization, team-member, and JWT checks.
  • Logs sanitized internal details at HTTP, realtime, and MCP response boundaries.
  • Adds regression coverage for response contracts, logging, JWT handling, and MCP sampling.

Confidence Score: 4/5

The hardening behavior appears functionally sound, but the repository’s compatibility requirement must be satisfied with a user-controlled rollout path before merging.

The fixed message prevents policy details from reaching the reviewed client paths and preserves response status and type contracts, but it unconditionally changes established error text in violation of an explicit compatibility rule. The earlier JWT redaction, TypedDict, test-docstring, mutable-set, shared-test-state, and failed-reset threads were manually resolved without explanation; the JWT scope-shape concern was correctly conceded after the existing stringification behavior and parity test were confirmed.

Files Needing Attention: litellm/proxy/auth/model_access_denied.py, litellm/proxy/auth/auth_checks.py, litellm/proxy/auth/handle_jwt.py

Important Files Changed
Filename Overview
litellm/proxy/auth/model_access_denied.py Defines the fixed public denial message and specialized JWT exception, but removes the prior user-controlled compatibility path.
litellm/proxy/auth/auth_checks.py Converts principal model-access failures to specialized exceptions while unconditionally changing their client-visible messages.
litellm/proxy/auth/handle_jwt.py Redacts role and scope allowlists while preserving their status codes and response forms.
litellm/proxy/auth/auth_exception_handler.py Preserves internal JWT denial context while converting it to the proxy exception contract.
litellm/proxy/proxy_server.py Logs sanitized denial details at HTTP and realtime response boundaries.
litellm/proxy/_experimental/mcp_server/sampling_handler.py Returns the generic message for specialized access denials and logs their sanitized internal reason.

Reviews (6): Last reviewed commit: "fix(mcp): return fixed client message on..." | Re-trigger Greptile

Comment thread litellm/proxy/auth/auth_checks.py Outdated
Comment thread litellm/proxy/proxy_server.py Outdated
Comment thread tests/test_litellm/proxy/auth/test_auth_checks.py Outdated
Comment thread tests/test_litellm/proxy/test_proxy_server.py Outdated
Comment thread litellm/proxy/auth/auth_checks.py Fixed
@codecov

codecov Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

… log, await dashboard saves

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread litellm/proxy/auth/auth_checks.py Outdated
yassin-berriai and others added 2 commits September 15, 2026 23:36
… boundary

Move the WARNING that carries the internal denial detail out of the message formatter and into the auth exception handler. The denial exceptions now carry internal_message so access-group probes and fallback paths that catch and recover from the denial no longer log a false denial for an allowed request

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread litellm/proxy/auth/auth_exception_handler.py Outdated
yassin-berriai and others added 3 commits September 16, 2026 00:12
… boundary

Post-auth denials from can_key_call_resolved_model (per-request alias
rewrite, MCP sampling, realtime) never reach the auth exception handler,
so the internal allowlist reason was dropped when
model_access_denied_message was set. Log it once from the ProxyException
response handler and the realtime rejection path instead, and convert
JWT ModelAccessDeniedHTTPException into the specialized ProxyException so
the same boundary covers it

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ing denials

MCP sampling catches the denial itself and returns ErrorData, so the central ProxyException handler never sees it. Log the sanitized internal reason there and share the CR/LF stripping through ModelAccessDeniedProxyException.sanitized_internal_message

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/auth/auth_exception_handler.py
…n conversion

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

yassin-berriai and others added 2 commits September 16, 2026 01:44
…h a fixed clean client message

Drop the model_access_denied_message setting, its {model} template, the DB
override entry and the Admin UI field. Model access denials now always return
the fixed client message while the allowlist diagnostic is logged at the final
HTTP, realtime and MCP boundaries

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration devin-ai-integration Bot changed the title feat(proxy): configurable client-facing model access denied message fix(proxy): hide model allowlist from client-facing model access denied errors Sep 16, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

Comment on lines +4175 to +4180
internal_message: Final = (
f"{object_type} not allowed to access model. This {object_type} can only access models={models}. "
f"Tried to access {model}"
)
raise ModelAccessDeniedProxyException(
message=model_access_denied_client_message(model=model),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unconditional Error Contract Change

These model-access denials now always replace the established client-visible details with a fixed generic message. The repository requires backward-incompatible behavior changes to have a user-controlled flag, so an opt-in setting or another compatibility path must be provided before merging. The same unconditional change also affects the related user, team-member, and JWT denial paths.

Rule Used: What: avoid backwards-incompatible changes without user-controlled flags Why: This breaks current behaviour for users using existing functionality Example of BAD: this PR (#22164) introduced run_post_custom... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b085a3c. Configure here.

yassin-berriai and others added 2 commits September 16, 2026 16:27
…denied_message

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	tests/test_litellm/proxy/auth/test_handle_jwt.py
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@yassin-berriai
yassin-berriai merged commit f0474bb into main Sep 16, 2026
91 checks passed
@yassin-berriai
yassin-berriai deleted the litellm_model_access_denied_message branch September 16, 2026 16:38
yuneng-berri added a commit that referenced this pull request Sep 17, 2026
leilei3167 pushed a commit to leilei3167/litellm that referenced this pull request Sep 17, 2026
…iAI#39996 and BerriAI#41310

Every one of these fails on main's own scheduled CircleCI run with the same
assertion as on any PR, and each traces to a merged behavior change that
never updated the test that pinned the old behavior

- tests/integration/_support/client.py: BerriAI#41311 made /key/info serve deleted
  keys from the archive with status deleted, so the scenario teardown asserts
  the live row is gone and the readback reports deleted instead of a 404.
  This alone accounts for nine integration-management and one
  integration-providers failure
- tests/integration/authorization/test_warmed_policy.py: BerriAI#39996 made team
  admins unable to edit any team field unless a proxy admin allow-lists it,
  and tpm_limit is the only field it accepts today. The demotion test now
  enables tpm_limit for the scenario and edits that instead of team_alias
- tests/llm_responses_api_testing/test_base_responses_api_streaming_iterator.py:
  BerriAI#41337 reads usage off the terminal response and copies the event when it
  is missing, which a Mock(spec=ResponsesAPIResponse) cannot survive. The
  four mocks now carry a usage object
- tests/test_openai_endpoints.py: BerriAI#41310 lengthened the access-denied
  message, and the test matched against the ExceptionInfo repr, which
  saferepr truncates in the middle. It now matches the exception text
- tests/local_testing/test_text_completion.py: Together no longer serves
  Qwen2-1.5B serverless, the cheapest cost-map row. The test mocks the
  completions call and asserts the request litellm builds, so a vendor
  catalog rotation cannot fail it again

test_router_fallbacks_with_cooldowns_and_dynamic_credentials is deliberately
untouched: it passes and fails on main with identical code, and the failing
path is a product question about whether dynamic-credential 429s cool down
yuneng-berri added a commit that referenced this pull request Sep 17, 2026
The team access-group fixture polled a 403 until its message enumerated
the team's allow-list, because registering a team-scoped deployment
appends that deployment to the list and the fixture has to wait for the
reset to land. #41310 replaced that message with a fixed client-facing
one, so the poll never matched and both tests errored at setup

The allow-list is now read back from /team/info until it holds exactly
the access group
yuneng-berri added a commit that referenced this pull request Sep 17, 2026
The team access-group fixture polled a 403 until its message enumerated
the team's allow-list, because registering a team-scoped deployment
appends that deployment to the list and the fixture has to wait for the
reset to land. #41310 replaced that message with a fixed client-facing
one, so the poll never matched and both tests errored at setup

The allow-list is now read back from /team/info until it holds exactly
the access group
yuneng-berri added a commit that referenced this pull request Sep 17, 2026
The team access-group fixture polled a 403 until its message enumerated
the team's allow-list, because registering a team-scoped deployment
appends that deployment to the list and the fixture has to wait for the
reset to land. #41310 replaced that message with a fixed client-facing
one, so the poll never matched and both tests errored at setup

The allow-list is now read back from /team/info until it holds exactly
the access group
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants