fix(router): pin batch, file, and fine-tuning job ids to their owning model group on fallback - #38742
Conversation
… model group on fallback
Greptile SummaryThe PR prevents resource-specific batch, file, and fine-tuning operations from falling back to unrelated provider groups while retaining normal cross-group fallback for ordinary requests.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/router_utils/fallback_event_handlers.py | Adds handler-gated provider-resource detection so resource identifiers pin fallback attempts to their owning model group. |
| tests/test_litellm/router_utils/test_fallback_event_handlers.py | Covers batch, file, and fine-tuning pinning, same-group retries, generic dispatch, and stray completion fields. |
| tests/test_litellm/test_router.py | Verifies end-to-end router behavior prevents batch cancellation from reaching a foreign fallback provider. |
| tests/llm_responses_api_testing/test_openai_responses_api.py | Updates a Responses API test to explicitly disable reasoning so temperature remains applicable. |
| tests/openai_endpoints_tests/test_e2e_openai_responses_api.py | Uses a non-reasoning model so the invalid-temperature test continues exercising provider rejection. |
Reviews (4): Last reviewed commit: "fix(router): arm the provider-scoped fal..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
PR #38593 stopped forwarding temperature to reasoning models, which left test_extra_body_merges_with_request_data raising UnsupportedParamsError and test_bad_request_bad_param_error no longer getting a rejection from OpenAI because drop_params now eats the param. Both repairs are the same hunks PR #38739 carries, so the branches merge clean in either order
|
bugbot run |
There was a problem hiding this comment.
✅ 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 8cf090b. Configure here.
…itellm_batch_id_fallback_pin # Conflicts: # tests/openai_endpoints_tests/test_e2e_openai_responses_api.py
|
bugbot run |
There was a problem hiding this comment.
✅ 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 2d11870. Configure here.
efe51da
into
litellm_internal_staging
TLDR
Problem this solves:
batch_id, yet fallback still crossed model groupsfile_idandfine_tuning_job_idcallsHow it solves it:
input_file_idfile_id-style field on an ordinary completion request cannot silently disable that request's cross-group fallbacksAn earlier tip also carried repairs for two responses-suite tests that went stale on staging when PR #38593 gated temperature for reasoning models; PR #38748 has since landed its own versions of those repairs on staging, and the merge commit here adopts them, so this PR no longer touches those files
User Flow
Before: an admin canceling a batch during an OpenAI key outage watches the gateway also try the cancel against their unrelated Azure group before the real error returns
http://litellm-domain/v1/fileswithpurpose=batchandtarget_model_names=batch-owner(their OpenAI-backed group), and get back a long scrambled gateway file idhttp://litellm-domain/v1/batcheswith that file id, and get back a long scrambled batch id with statusvalidating; the batch runs in their OpenAI accountbatch-ownerbreaks, and they cancel: POSThttp://litellm-domain/v1/batches/{batch_id}/cancelinvalid_api_key, but only after an extra provider round trip: the gateway log showsFalling back to model_group = batch-neighborand then an ERROR carrying Azure's 404 "The requested job ... does not exist under the account", because their Azure resource was really asked to cancel a batch id it never issuedAfter: the same cancel never leaves the group that owns the batch, so the real error comes straight back and Azure never sees the foreign id
http://litellm-domain/v1/fileswithpurpose=batchandtarget_model_names=batch-owner, and get back a long scrambled gateway file idhttp://litellm-domain/v1/batcheswith that file id, and get back a long scrambled batch id with statusvalidatingbatch-ownerbreaks, and they cancel: POSThttp://litellm-domain/v1/batches/{batch_id}/cancelinvalid_api_key, now after a single call to OpenAI: the gateway log showsSkipping fallback to model_group = batch-neighbor: request names a resource owned by model_group = openai-batch-dep, and their Azure resource gets no call at allRelevant issues
Linear ticket
Resolves LIT-6361
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
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@greptileaito 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
Live before/after run against real OpenAI and a real Azure OpenAI resource, no mocks. Both legs ran the identical flow and topology, differing only in the commit the proxy was booted from: Before at the merge base, After at this PR's tip
Shared setup: config defines
batch-owner(openai/gpt-5.4-mini,model_info.id: openai-batch-dep) andbatch-neighbor(azure/gpt-4o-minion a real Azure resource) withrouter_settings.default_fallbacks: ["batch-neighbor"]. Every proxy boot used--num_workers 2. Phase 1 (good config, one process) served the upload and batch create; phase 2 rebooted with only thebatch-ownerapi_key replaced bysk-invalid-simulated-outage, as two separate processes on two random ports sharing the leg's Postgres DB, with the cancel sent to one process and the file-content read to the other, so state written through the phase-1 process was read back through both phase-2 processes. Both legs' real batches were canceled directly against OpenAI afterward with the real keyBefore (ae7e50f)
Upload and batch create (owner group healthy)
Upload the batch input through the phase-1 proxy (port 33456)
Create the batch with that unified file id
Batch cancel with the owner key broken
Cancel through phase-2 process B (port 35802); the caller gets the owner's 401
Read the gateway log the admin sees: the fallback fired and the Azure resource was really asked to cancel a batch id it never issued
Grep counts in this process's log:
Falling back to model_group1,Skipping fallback to model_group0,JobNotFound5,mateo-resource(the Azure host) 10File content with the owner key broken
Fetch content through phase-2 process C (port 41415); the foreign provider's 404 reaches the caller verbatim inside the error body
This process's log confirms the OpenAI provider file id was sent to the Azure api_base
Grep counts in this process's log:
Falling back to model_group1,Skipping fallback to model_group0,mateo-resource7After (8cf090b)
The current tip 2d11870 is a conflict-resolution merge of the base that only adopts PR #38748's versions of two responses-suite test files;
git diff 8cf090b368..2d11870793 -- litellm/ tests/test_litellm/is empty, so this proof is exactly what the tip runsUpload and batch create (owner group healthy)
Upload the batch input through the phase-1 proxy (port 27446)
Create the batch with that unified file id
Batch cancel with the owner key broken
Cancel through phase-2 process B (port 27447); identical caller-visible 401, now after a single owner-side attempt
The gateway log now shows the pin instead of the fallback, and zero foreign-call markers
Grep counts in this process's log:
Falling back to model_group0,Skipping fallback to model_group1,JobNotFound0,mateo-resource0,does not exist under the account0File content with the owner key broken
Fetch content through phase-2 process C (port 27448); the error body now carries the owner's real 401 where the foreign 404 used to be
This process's log shows the same pin line and no foreign call
Grep counts in this process's log:
Falling back to model_group0,Skipping fallback to model_group1,mateo-resource0The real batch behind the After leg was canceled directly against OpenAI afterward with the real key (200, status
cancelling)Stray top-level file_id on a chat completion (the handler-name gate)
An ordinary
/v1/chat/completionsrequest carrying a stray top-levelfile_idextra body field must keep its cross-group fallbacks. On the broken-owner config, both legs got the same requestBefore (ae7e50f, port 41400) and After (8cf090b, port 59632) returned byte-identical bodies: the owner's 401, then
Error doing the fallback: litellm.NotFoundError: AzureException NotFoundError, proving the fallback really crossed tobatch-neighborand reached the Azure resource on both legs. The After log showsFalling back to model_group = batch-neighborand zeroSkipping fallbacklines. At this PR's previous tip (0b14897) the same request instead retried the owner's own broken key (Error doing the fallback: litellm.AuthenticationError ... sk-inval), silently losing the cross-group fallback; the handler-name gate in 8cf090b removes that regressionObservations from the run (things the diff alone would not show):
internal_server_error(pre-existing, unchanged)Type
🐛 Bug Fix
Caveats (if any)
Vector store file operations dispatch through generically named router aliases (
acreate,adelete,aretrieve,aretrieve_content), so they are deliberately left out of the pin's handler allowlist and keep today's fallback behavior.Router.aretrieve_batchfans out to every deployment itself and never enters the fallback wrapper, so it needs no pinFinal Attestation
litellm/andtests/test_litellm/trees are byte-identical to it)Note
Medium Risk
Changes core router fallback gating for batch/file/fine-tuning APIs; misclassified handlers could block valid fallbacks or allow incorrect cross-group calls, though tests target the main paths.
Overview
Stops router fallbacks from sending batch, file, and fine-tuning job operations to another model group when the request carries provider-owned ids (
batch_id,file_id,fine_tuning_job_id, alongside existinginput_file_id/training_file). Cross-group fallback is skipped so callers get the owning deployment’s error instead of a wasted foreign 404 and leaked ids.The pin only arms for known file/batch/fine-tuning handlers, checking both
original_functionandoriginal_generic_function(generic API dispatch). Chat completions with stray top-level fields likefile_idstill get cross-group fallbacks. Same-group retries (e.g. order-based failover) are unchanged.Tests cover unit fallback behavior and an integration case where
acancel_batchdoes not hit the default fallback provider.Reviewed by Cursor Bugbot for commit 2d11870. Bugbot is set up for automated code reviews on this repo. Configure here.