fix(proxy/batches): fail closed with 503 when the managed-file lookup errors

A lookup exception previously fell back to dispatching the unresolved
unified token, which defeats the fail-closed guarantee: the token still
reaches the provider and can hit the same publishers-segment IndexError
the resolution prevents. Treat a lookup error like the missing-row case
and fail closed, but with a retryable 503 since the condition is
transient. No-database and no-storage_url rows still fall back unchanged
This commit is contained in:
Yucheng Zhu 2026-07-24 17:06:46 -07:00
parent 18dd984902
commit 891c6e4123
2 changed files with 19 additions and 12 deletions

View file

@ -49,11 +49,12 @@ async def _resolve_managed_input_file_storage_url(input_file_id: str) -> "str |
Provider batch handlers (e.g. Vertex AI, which parses a `publishers/`
segment out of the file URI) need a real storage location; the opaque
unified token crashes them. Returns None when there is no database, the
lookup fails, or the row has no storage_url yet, so callers fall back to
the original id (which the managed-files deployment hook can still map).
Raises a 404 when no managed-file row exists, since the token cannot be
resolved and dispatching it would crash the provider.
unified token crashes them. Returns None only when there is no database or
the row has no storage_url yet, so callers fall back to the original id
(which the managed-files deployment hook can still map). Fails closed
rather than dispatch a token that cannot be resolved: 404 when no
managed-file row exists, 503 when the lookup itself errors so the caller
can retry.
"""
from litellm.proxy.proxy_server import prisma_client
@ -63,7 +64,10 @@ async def _resolve_managed_input_file_storage_url(input_file_id: str) -> "str |
db_file = await ManagedFileRepository(prisma_client).table.find_first(where={"unified_file_id": input_file_id})
except Exception as e:
verbose_proxy_logger.warning("create_batch: managed file lookup failed for %s: %s", input_file_id, e)
return None
raise HTTPException(
status_code=503,
detail={"error": "Could not resolve managed file; please retry"},
)
if db_file is None:
raise HTTPException(
status_code=404,

View file

@ -561,10 +561,10 @@ async def test_create__unified_file_id_resolves_real_storage_url(harness):
@pytest.mark.asyncio
async def test_create__unified_file_id_db_error_falls_back_to_raw_id(harness):
"""A lookup failure must not abort batch creation. Resolution is best-effort
crash prevention, so on a database error it falls back to dispatching the
original id, which the managed-files deployment hook can still map."""
async def test_create__unified_file_id_db_error_fails_closed_503(harness):
"""A lookup error leaves the token unresolved, so it fails closed with a
retryable 503 rather than dispatching the opaque id into the provider crash
it cannot parse. Nothing is dispatched."""
set_body(
harness,
{
@ -585,9 +585,12 @@ async def test_create__unified_file_id_db_error_falls_back_to_raw_id(harness):
patch.object(proxy_server, "prisma_client", MagicMock()),
patch.object(endpoints, "ManagedFileRepository", fake_repo_cls),
):
await call_create(harness)
with pytest.raises(ProxyException) as exc:
await call_create(harness)
assert harness.router_kwargs()["input_file_id"] == "litellm_proxy_unified_id"
assert exc.value.code == "503"
harness.router_acreate.assert_not_called()
harness.litellm_acreate.assert_not_called()
@pytest.mark.asyncio