chore(release): backport #34189, #36011 to stable/1.94.x and cut 1.94.3 - #36317
Conversation
…ed model The URL-destination check previously ran over request-body fields only. The per-field logic moves into reject_url_valued_destination(field, value) so a deployment name resolved from the request path runs the same check against the same admin allowlist. BREAKING CHANGE: a deployment name supplied in the request path that parses as an http/https destination is now refused. Add the host to `provider_url_destination_allowed_hosts` in litellm_settings to keep it working. (cherry picked from commit fc4be70)
…ON form is parsed Multipart callers express nested metadata as flat bracket-notation keys, which reach the request-body check as literal keys rather than as a metadata dict. The check now rebuilds them with the same helper the endpoints use, so both encodings are handled identically and cannot drift apart. BREAKING CHANGE: a multipart field such as `litellm_metadata[api_base]` is now subject to the same request-body parameter rules as its JSON equivalent. Set `general_settings.allow_client_side_credentials`, or the deployment's `configurable_clientside_auth_params`, to keep passing these. (cherry picked from commit 5b2c92d)
…verrides When a connection test names a model that resolves to a configured deployment, that deployment's routing and credential parameters are authoritative. A request supplying a complete connection of its own is unaffected. BREAKING CHANGE: /health/test_connection no longer lets a request replace the routing or credential parameters of a configured model it names. Supply the full connection parameters instead of naming a configured model. (cherry picked from commit e5effcb)
… test sets its own A request that supplies its own connection fields describes a connection of its own, so the configured deployment's credentials are no longer merged underneath it. Anything the request leaves unset still comes from the configuration, so naming a configured model and testing it as configured is unchanged, and adding a second deployment for an already-configured name works as before. Replaces the earlier outright rejection, which also refused requests that supplied a complete connection of their own. (cherry picked from commit b468acb)
…credential reuse The proxy-wide opt-in that already governs callers supplying their own connection parameters now also governs whether a connection test may pair a request-supplied endpoint with the configured deployment's credentials. Off by default, which keeps configured credentials scoped to the endpoint the configuration names; on, the previous merge behaviour is available unchanged. (cherry picked from commit 59173c3)
…tials it names A connection test that redirects the destination already leaves the configured credentials behind. It kept litellm_credential_name, which names the same stored secrets and is resolved further down the call, so the reference is now dropped with them. A request that sets no connection fields of its own is unaffected, which is how the Admin UI tests a configured model. (cherry picked from commit 298fb8c)
Greptile SummaryThis patch cuts LiteLLM 1.94.3 and backports request-destination, fallback-validation, multipart metadata, and health-check credential hardening
Confidence Score: 4/5The PR appears safe to merge after addressing the non-blocking source-comment convention violation The changed request validation, authorization traversal, provider URL handling, and health-check credential logic have no established blocking failure; the retained pypdf advisories predate this patch Files Needing Attention: litellm/proxy/health_endpoints/_health_endpoints.py
|
| Filename | Overview |
|---|---|
| litellm/proxy/auth/auth_utils.py | Adds shared recursive fallback traversal, nested multipart metadata validation, Vertex credential filtering, and URL-valued fallback rejection |
| litellm/proxy/auth/user_api_key_auth.py | Reuses the shared fallback traversal for key-level model authorization without a confirmed authorization regression |
| litellm/proxy/health_endpoints/_health_endpoints.py | Separates configured connection credentials from request overrides, but adds source documentation contrary to the repository convention |
| litellm/proxy/litellm_pre_call_utils.py | Extracts a reusable destination guard that recognizes provider-prefixed and comma-separated URL candidates |
| litellm/proxy/common_request_processing.py | Applies destination validation to path-supplied model values before normal request routing |
| litellm/proxy/image_endpoints/endpoints.py | Extends the path-model destination guard to image generation |
| litellm/litellm_core_utils/url_utils.py | Adds candidate extraction for raw, provider-prefixed, and comma-separated destination values |
| litellm/llms/huggingface/embedding/handler.py | Restricts direct URL detection to actual HTTP and HTTPS prefixes |
| litellm/llms/oobabooga/chat/oobabooga.py | Restricts direct URL detection to actual HTTP and HTTPS prefixes for chat and embedding calls |
| uv.lock | Refreshes release metadata without changing dependency versions, including the pre-existing pypdf version |
Reviews (1): Last reviewed commit: "chore: refresh uv.lock for 1.94.3" | Re-trigger Greptile
|
|
||
|
|
||
| def _config_base_for_health_check( | ||
| config_params: Mapping[str, object], | ||
| request_params: Mapping[str, object], | ||
| allow_client_side_credentials: bool = False, | ||
| ) -> dict[str, object]: | ||
| """Return the configured parameters to merge under a connection-test request. | ||
|
|
||
| A request that sets its own connection fields describes a connection of its | ||
| own, so the configuration's credentials are not carried into it: they belong | ||
| to the endpoint the configuration names. Anything the request does not set | ||
| still comes from the configuration, which is what lets a request name a | ||
| configured model and test it as configured. | ||
|
|
||
| ``litellm_credential_name`` is dropped alongside the literal credential | ||
| fields: it names a stored credential that ``load_credentials_from_list`` | ||
| resolves into the same secrets further down the call, so leaving it in place | ||
| would reintroduce them by reference. | ||
|
|
There was a problem hiding this comment.
New source documentation violates convention
The new multi-paragraph docstring, along with similar explanatory additions elsewhere in this patch, violates the repository rule against adding source-code comments and increases maintenance prose that the implementation must keep synchronized.
Context Used: CLAUDE.md (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!
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
make test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
CI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Screenshots / Proof of Fix
Type
🐛 Bug Fix
Changes
Patch release
1.94.3on top ofv1.94.2. Every code commit is acherry-pick -xof a commit already merged tolitellm_internal_staging, so nothing here is new work written against the stable line.Two upstream PRs are backported.
#36011landed on staging as a real merge commit rather than a squash, so there is no single SHA to pick; its six constituent commits are picked individually. That was verified equivalent tocherry-pick -m 1of the merge — the merge diff (c898d341^1..c898d341) and the branch range diff (f047124b..298fb8ce) share the identical patch-id8979711cefee729f, so the merge absorbed no staging drift.chore(proxy): clean up request parameter validation and provider destination handling (#34189)065faf6e69fix(proxy)!: share one destination check between body and path-supplied model(#36011)fc4be70a37fix(proxy)!: parse bracket-notation form metadata the same way its JSON form is parsed(#36011)5b2c92d749fix(health)!: let configured deployment parameters win over request overrides(#36011)e5effcb861fix(health): stop inheriting configured credentials when a connection test sets its own(#36011)b468acb31cfeat(health): let allow_client_side_credentials re-enable configured-credential reuse(#36011)59173c3a20fix(health): drop a stored-credential reference along with the credentials it names(#36011)298fb8ce56Plus
bump: version 1.94.2 → 1.94.3andchore: refresh uv.lock for 1.94.3.Ordering is mandatory:
#36011callsiter_request_fallback_targetsandreject_url_valued_destination, both introduced by#34189and neither present on the line before it.Also requested, already on the line — no pick needed
Nine PRs were requested for this patch. Seven were already present and were verified rather than re-picked:
#30585,#30867,#31905,#32093,#324055d4c4d0fce, 2026-07-18), so carried in at the cut#35835,#35844#36237, merged 2026-08-08#36011is a breaking change and this patch carries it. Each is the hardening itself and cannot be opted out of individually, but each has a documented way to restore the prior behavior where it was intentional.provider_url_destination_allowed_hostsunderlitellm_settingsto keep it working.litellm_metadata[api_base]is now subject to the same request-body parameter rules as its JSON equivalent. Setgeneral_settings.allow_client_side_credentials, or the deployment'sconfigurable_clientside_auth_params, to keep passing these./health/test_connectionno longer lets a request replace the routing or credential parameters of a configured model it names. Supply the full connection parameters instead of naming a configured model, or setgeneral_settings.allow_client_side_credentials: trueto restore the previous merge behavior unchanged.Naming a configured model and testing it as configured is unchanged — that is how the Admin UI tests a model, and that flow is explicitly covered by a regression assertion.
Worth flagging for risk assessment:
#36011has never shipped in a GA release (onlyv1.97.0-dev.2).#34189shipped inv1.95.0.Adaptation notes
Four of the seven picks needed adaptation. All four are context divergence from staging lint/typing migrations that post-date the line's cut. None changes behavior.
#34189—tests/code_coverage_tests/recursive_detector.py. Staging'sIGNORE_FUNCTIONScontains_redact_scanned_contentfrom an unrelated PR, and no such function exists on this line. Only the entry#34189actually contributes,_iter_fallback_targets, was added.fc4be70a37—litellm/proxy/litellm_pre_call_utils.py. The deletion hunk targetedallowed_hosts: Final = getattr(...); this line's copy has noFinal(staging's enforce-Final-on-locals migration post-dates the cut). Resolved against the line's form, andFinaladded to that module'stypingimport for the newreject_url_valued_destination().model: Optional[str]also retained over staging'smodel: str | Noneinimage_endpoints/endpoints.py.e5effcb861—litellm/proxy/health_endpoints/_health_endpoints.py. Staging importsIterablefromcollections.abc; this line keeps it intyping.Mappingwas added to the line's existingtypingimport instead. Load-bearing:Mappingappears in the_reject_banned_param_overridessignature, which is evaluated at definition time, so the module would not have imported without it.b468acb31c— same file. Introduces_CONFIG_CONNECTION_FIELDS: Final[frozenset[str]]at module level, where annotations are evaluated at runtime. This hunk cherry-picked with zero conflict, and withoutFinalin the import the proxy would have failed to boot withNameErroron import.Finalwas added and the pick amended so the fix sits in the commit that needs it.The last one is the notable catch: git reported no conflict and the pick-owned tests would not have caught it. Only importing every touched module under the branch's own interpreter did.
Verification
Symbol closure. Every touched module imported for real in the branch's venv;
reject_url_valued_destination,iter_request_fallback_targets,provider_url_destination_candidates,_config_base_for_health_checkand_CONFIG_CONNECTION_FIELDSall resolve. New paths checked for reachability, not just existence —image_endpoints/endpoints.pyimports the guard at:73and calls it at:101.Tests owned by the picks (7 files on the branch, the 6 that exist on the base):
stable/1.94.xtipBlast radius —
tests/test_litellm/proxy, two passes on each tree:stable/1.94.xtipPass 1 showed +5 on the branch; that is
xdist --dist loadfileorder dependence, not regression. All 8 branch-only failures pass in isolation on the branch, all 3 base-only failures pass in isolation on the base, and re-running the identical command flipped the counts — on pass 2 the untouched base is worse than the branch.Format and lint, run under this line's own config:
ruff format --checkreports the same single pre-existing offender (proxy/management_endpoints/credential_migration.py) on both trees, andruff check --statisticsis byte-identical at 1017 errors across the same 14 rule codes. Zero delta on both.Behavior, red/green. A standalone reproducer run under each tree's interpreter:
v1.94.2HTTPException; allowlist re-opens itThe middle row is the strongest: on
v1.94.2both symbols exist and the code path is fully reached — the check simply fails to blockmetadata[api_base]while blocking its JSON twin. That is the issue reproduced live rather than inferred from a missing symbol.Dependencies. No dependency moves in this patch. A grype sweep of the lock reports 0 Critical / 0 High / 3 Medium on the base, on
litellm_internal_staging, and on this branch — identical at every stage. The two in-rangepypdf6.14.2 advisories (GHSA-fp3f-mc75-235c,GHSA-fwg2-594c-jp42, fixed in 6.15.0) are deliberately not bumped: staging also resolves 6.14.2, so moving the stable line alone would put it ahead of mainline and a customer upgrading off this patch would regress back into both. This ships at staging parity by design.diskcache5.6.3 has no fixed version released.uv.lockmoves by exactly two lines — the litellm version and theexclude-newerstamp thatexclude-newer = "3 days"re-materializes on every lock. Zero package version drift.Release notes for
v1.94.3are drafted and will follow in a separatelitellm-docsPR.