Skip to content

fix(proxy): stop /{provider}/v1/files from capturing /anthropic passthrough - #37412

Open
chelsealong wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
chelsealong:litellm_fix_anthropic_passthrough_files_shadowing
Open

fix(proxy): stop /{provider}/v1/files from capturing /anthropic passthrough#37412
chelsealong wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
chelsealong:litellm_fix_anthropic_passthrough_files_shadowing

Conversation

@chelsealong

Copy link
Copy Markdown

TLDR

Problem this solves:

How it solves it:

  • give /anthropic its own router, mounted ahead of the batches and files routers
  • every other provider prefix keeps today's behavior

User Flow

Before: a developer using the Anthropic SDK against the gateway cannot upload a document at all, so the model never sees their PDF

  1. They set base URL https://litellm-domain/anthropic and send POST https://litellm-domain/anthropic/v1/files with file=@plan.pdf, what client.beta.files.upload({ file }) emits
  2. They get 422 {"detail":[{"type":"missing","loc":["body","purpose"],"msg":"Field required"}]}, no file id, and nothing in their Anthropic account
  3. With no file id they cannot reference the document in POST https://litellm-domain/anthropic/v1/messages, so the answer ignores their PDF

After: the same upload reaches Anthropic instead of being intercepted locally

  1. They set base URL https://litellm-domain/anthropic and send the same POST https://litellm-domain/anthropic/v1/files with file=@plan.pdf, no extra fields
  2. The request reaches api.anthropic.com, so with a valid key they get back Anthropic's own file object and can reference its file_id in a later /anthropic/v1/messages call

Relevant issues

Fixes #37289

Linear ticket

Pre-Submission checklist

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

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • 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

Config used for both runs:

model_list:
  - model_name: claude-haiku-4-5
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY

general_settings:
  master_key: sk-local-repro-master

Run with ANTHROPIC_API_KEY=sk-ant-invalid-key-for-local-repro on purpose, same technique as the issue: a request LiteLLM answers itself fails 422 on purpose, a request that is forwarded to Anthropic fails 401 there instead.

Before (41de13a)

  1. POST /anthropic/v1/files:
$ curl -s -w "\nHTTP %{http_code}\n" -X POST http://127.0.0.1:4000/anthropic/v1/files \
    -H "x-api-key: sk-local-repro-master" -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: files-api-2025-04-14" -F "file=@repro.pdf;type=application/pdf"

{"detail":[{"type":"missing","loc":["body","purpose"],"msg":"Field required","input":null}]}
HTTP 422

After (75fbc4b)

  1. POST /anthropic/v1/files, now forwarded, 401 from Anthropic as expected with an invalid key:
$ curl -s -w "\nHTTP %{http_code}\n" -X POST http://127.0.0.1:4000/anthropic/v1/files \
    -H "x-api-key: sk-local-repro-master" -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: files-api-2025-04-14" -F "file=@repro.pdf;type=application/pdf"

{"type":"error","error":{"type":"authentication_error","message":"API key is invalid."},"request_id":null}
HTTP 401
  1. Control, /anthropic/v1/messages still forwards normally, same 401:
$ curl -s -X POST http://127.0.0.1:4000/anthropic/v1/messages \
    -H "x-api-key: sk-local-repro-master" -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{"model":"claude-haiku-4-5","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'

{"type":"error","error":{"type":"authentication_error","message":"API key is invalid."},"request_id":null}
HTTP 401

Unit test proof, git checkout HEAD~1 -- litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py litellm/proxy/proxy_server.py then running the new test:

$ uv run pytest tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py -k test_anthropic_passthrough_prefix_wins_over_native_provider_routes -q
...
FAILED ...[DELETE-/anthropic/v1/files/file-abc123] - AssertionError: assert 'delete_file' == 'anthropic_proxy_route'
FAILED ...[GET-/anthropic/v1/files/file-abc123] - AssertionError: assert 'get_file' == 'anthropic_proxy_route'
FAILED ...[GET-/anthropic/v1/files] - AssertionError: assert 'list_files' == 'anthropic_proxy_route'
FAILED ...[POST-/anthropic/v1/batches] - AssertionError: assert 'create_batch' == 'anthropic_proxy_route'
FAILED ...[POST-/anthropic/v1/files] - AssertionError: assert 'create_file' == 'anthropic_proxy_route'
5 failed, 1 passed, 132 deselected, 1 warning in 5.39s

Restoring the fix and re-running the full file:

$ uv run pytest tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py -q
...
138 passed, 3 warnings in 5.64s

Type

🐛 Bug Fix

Caveats (if any)

QA runbook

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…hrough

The native files and batches routers mount before the passthrough router, so
/anthropic/v1/files matched /{provider}/v1/files with provider="anthropic" and
422'd on the OpenAI-only `purpose` field instead of forwarding to Anthropic's
Files API. Give /anthropic its own router mounted ahead of batches and files,
mirroring the fix already applied to /openai_passthrough in BerriAI#36092.
@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR gives the Anthropic passthrough route a dedicated router mounted before the generic batches and files routers, preventing generic provider routes from capturing Anthropic SDK requests.

  • Moves /anthropic/{endpoint:path} onto anthropic_passthrough_router.
  • Registers the dedicated router ahead of generic files and batches routes.
  • Adds production-route-table tests covering Anthropic files, batches, and messages paths.

Confidence Score: 5/5

The PR appears safe to merge because the dedicated router corrects the intended route precedence without changing authentication or unrelated provider routing.

The Anthropic handler retains its route-level authentication and app middleware while moving ahead of only the generic files and batches routes that incorrectly captured /anthropic/*; the added tests inspect the production app’s actual registration order.

Important Files Changed

Filename Overview
litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Moves the existing authenticated Anthropic passthrough handler to a dedicated router without changing handler behavior.
litellm/proxy/proxy_server.py Mounts the Anthropic passthrough router before generic batches and files routers so the intended route wins FastAPI precedence.
tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py Adds regression coverage against the production route table for affected Anthropic files, batches, and messages paths.

Reviews (1): Last reviewed commit: "fix(proxy): stop /{provider}/v1/files fr..." | Re-trigger Greptile

@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed-hq

codspeed-hq Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing chelsealong:litellm_fix_anthropic_passthrough_files_shadowing (75fbc4b) with litellm_internal_staging (807e1da)

Open in CodSpeed

@IdoPort

IdoPort commented Aug 20, 2026

Copy link
Copy Markdown

Hitting the same symptom sequence (422 missing purpose → 500 files_settings is not set), but from a setup this issue won't fix, so flagging the gap:

Our case: we're not proxying to direct api.anthropic.com — we're relaying to a distinct Anthropic-compatible endpoint (Claude Platform on AWS), which needs its own target/api_base and an extra anthropic-workspace-id header alongside x-api-key. That's only configurable via an explicit pass_through_endpoints entry in config.yaml, not via the built-in /anthropic/{endpoint:path} catch-all (which is hardcoded to ANTHROPIC_API_BASE/ANTHROPIC_BASE_URL defaulting to api.anthropic.com, and to a plain ANTHROPIC_API_KEY).

Since the built-in catch-all owns the literal /anthropic prefix unconditionally, using a custom pass_through_endpoints config for a different Anthropic-compatible target means the prefix has to be something other than /anthropic (e.g. /claude-aws) — otherwise our config-declared target/headers are silently ignored and every request goes to api.anthropic.com with the wrong key instead.

But moving to a non-/anthropic prefix reproduces exactly this issue's shadowing: POST /claude-aws/v1/files still gets caught by /{provider}/v1/files, 422s on purpose, then 500s on files_settings not set.

The description says "every other provider prefix keeps today's behavior" — meaning even once merged, this specific case (a custom prefix pointing at a non-default Anthropic-compatible endpoint via pass_through_endpoints) stays broken, since the fix only re-privileges the literal /anthropic path.

Version note: we're seeing this on litellm==1.87.0 (PyPI upload 2026-06-02), which predates PR #30958 (merged 2026-07-02, blamed for the regression) by a full month — so either the regression predates that PR, or there's a second root cause with an identical symptom on older versions.

Ask: would it be possible to generalize the fix so any path explicitly declared in pass_through_endpoints always takes precedence over the generic /{provider}/v1/files (and batches) routers, rather than special-casing just /anthropic? That would cover custom-provider passthrough configs like ours without needing a per-prefix carve-out for every non-default endpoint.

Happy to provide a minimal repro config.yaml if useful.

@chelsealong

Copy link
Copy Markdown
Author

That's a separate root cause: custom pass_through_endpoints prefixes, not the /anthropic catch-all this PR fixes. Please file a new issue for it.

@IdoPort

IdoPort commented Aug 22, 2026

Copy link
Copy Markdown

Filed as #37925 — thanks for the quick triage.

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.

[Bug]: /{provider}/v1/files shadows the /anthropic passthrough → 422 "purpose Field required"

2 participants