fix: resolve unified_file_id to real storage_url before dispatching batch create - #34260
Conversation
…atch create litellm.create_batch() against a Vertex AI-backed model crashes with an opaque error when the input file was uploaded as a LiteLLM-managed 'unified file' (multi-model file upload). The base64-encoded unified_file_id token is a LiteLLM-internal identifier, not a real provider-side file reference, but the batches_endpoints create_batch handler forwards it unchanged to llm_router.acreate_batch() / litellm.acreate_batch() for the unified_file_id branch. Provider-specific code that expects a real file location (e.g. Vertex AI's batch transformation, which parses a 'publishers/' segment out of the GCS URI) then fails on the opaque token. Resolve the unified_file_id to its real backend location (LiteLLM_ManagedFileTable.storage_url) before dispatch, mirroring the same lookup already used by the files retrieve/download endpoints for managed files. Falls back to the previous (unchanged) behavior if no managed-file record exists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Greptile SummaryThis PR resolves unified file IDs before batch creation. The main changes are:
Confidence Score: 4/5The managed-file authorization boundary and incomplete resolution paths need fixes before merging.
litellm/proxy/batches_endpoints/endpoints.py
|
| Filename | Overview |
|---|---|
| litellm/proxy/batches_endpoints/endpoints.py | Adds storage URL resolution, but omits ownership checks and leaves several reachable failure paths. |
| tests/test_litellm/proxy/batches_endpoints/test_endpoints.py | Adds focused mocked tests for successful resolution and missing-row fallback. |
Reviews (1): Last reviewed commit: "fix: resolve unified_file_id to real sto..." | Re-trigger Greptile
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
|
We chased what looks like the same deterministic The crash isn't in the unified-file-id dispatch. It happens after the Vertex job is successfully created, when Two data points that made us drop the managed-files theory:
If your 7/7 repro ran with a Model Armor guardrail applied ( Full analysis, repro, and source references: #34390 |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Thanks @oginskis! You're right that Model Armor post-call scanning causes a separate 500 on batch responses (#34390). However, this PR fixes a different, earlier crash: when using Managed Files ( Resolving |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Relevant issues
litellm.create_batch()against a Vertex AI-backed model deterministicallycrashes when the input file was uploaded as a LiteLLM-managed "unified file"
(i.e. a batch input file uploaded once and usable across multiple target
models). Reproduced 7/7 in our own environment.
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
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
Root cause, traced end-to-end through the real request path
(
proxy/batches_endpoints/endpoints.py::create_batch->router.py::_acreate_batch->llms/vertex_ai/batches/transformation.py):when
input_file_idis a base64-encodedunified_file_id(LiteLLM'sinternal token for a managed, multi-model file upload -
litellm_proxy:application/octet-stream;unified_id,...;target_model_names,...),the
create_batchhandler resolves the targetmodelfrom it but forwardsthe unified_file_id itself, unchanged, as
input_file_idall the waydown to the provider-specific handler. Vertex AI's
_get_model_from_gcs_fileexpects a realgs://.../publishers/...URI anddoes an unguarded
.split("publishers/")[1]- which raisesIndexError: list index out of rangeon the opaque base64 string, surfacedto the caller as an opaque 500.
Before (current code, at commit
a65b83d,using the real transformation function with a realistic unified_file_id -
the exact shape produced by a real managed-file upload):
This is the same failure class (an unguarded string/dict access on an
internal token that was never resolved to its real backend value) as the
opaque 500 seen in production; the
create_batchproxy endpoint forwardsthis same unified_file_id string to this exact function, unchanged.
After (this PR's code): the
create_batchendpoint now resolves theunified_file_idto its real storage location(
LiteLLM_ManagedFileTable.storage_url) before dispatch, so the Vertexhandler always receives a real
gs://.../publishers/...URI:No crash. Falls back to the previous (unchanged) behavior if no managed-file
DB record exists, so this only changes behavior for the exact case that was
previously broken.
Also covered by the 2 new regression tests in
tests/test_litellm/proxy/batches_endpoints/test_endpoints.py(
test_create__unified_file_id_resolves_real_storage_url,test_create__unified_file_id_no_managed_file_record_falls_back_to_raw_id),confirmed passing alongside all 73 pre-existing tests in that file (75
passed, 2 xfailed total).
Type
🐛 Bug Fix
Changes
litellm/proxy/batches_endpoints/endpoints.py: in theunified_file_idbranch of
create_batch, resolve the base64unified_file_idto its realbackend storage location (
LiteLLM_ManagedFileTable.storage_url) beforecalling
llm_router.acreate_batch(), mirroring the same lookup alreadyused by the files retrieve/download endpoints for managed files. No
change if there's no managed-file DB record (falls back to prior
behavior).
tests/test_litellm/proxy/batches_endpoints/test_endpoints.py: 2 newregression tests covering the resolved-storage-url path and the
no-DB-record fallback.
Final Attestation
regressions in the respective real-world customer use-cases are not
possible after this PR