Skip to content

fix(passthrough): forward all multipart files with repeated field names#31391

Merged
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_multipart_file_upload
Jun 27, 2026
Merged

fix(passthrough): forward all multipart files with repeated field names#31391
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_multipart_file_upload

Conversation

@shivamrawat1

@shivamrawat1 shivamrawat1 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Fixes LIT-4043

Relevant issues
Fixes passthrough multipart uploads dropping files when a client sends several parts under the same field name (e.g. multiple -F file=@...)

Summary
Passthrough multipart forwarding in make_multipart_http_request used form.items() and a files dict keyed by field name. Starlette's FormData.items() keeps only the last value for duplicate keys, and a dict cannot hold multiple entries under the same key anyway, so only one file reached the upstream backend

The handler now reads form.multi_items(), builds files as a list of (field_name, (filename, content, content_type)) tuples for httpx, and groups repeated non-file fields into list values in data. httpx encodes both as separate multipart parts

Type
Bug Fix

Changes
litellm/proxy/pass_through_endpoints/pass_through_endpoints.py: iterate multi_items() and forward files as a list of tuples instead of a dict

tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py: update existing multipart tests for the new httpx shape; add test_make_multipart_http_request_forwards_repeated_fields regression test

Before:
Screenshot 2026-06-25 at 7 15 42 PM

After:
Screenshot 2026-06-25 at 7 15 32 PM

Passthrough multipart uploads used form.items() and a files dict, so only the last file under a repeated field name reached the upstream. Read multi_items() and send httpx a list of file tuples instead.

Co-authored-by: Cursor <[email protected]>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Shivam Rawat seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes multipart passthrough dropping all but the last file when a client sends multiple parts under the same field name (e.g. -F [email protected] -F [email protected]). The root cause was FormData.items() collapsing duplicate keys and a dict keyed by field name overwriting earlier entries.

  • make_multipart_http_request now reads form.multi_items() (a stable list in Starlette), builds files as a list of (field_name, (filename, content, content_type)) tuples, and groups repeated non-file fields into list values — both formats httpx encodes as separate multipart parts.
  • Tests are updated to pass FormData([...]) instead of a plain dict, assert on the list-of-tuples shape, and include a new regression test verifying that two files under the same field name and two non-file values under the same key are both forwarded.

Confidence Score: 5/5

Safe to merge — the change is a targeted, well-tested fix to multipart forwarding with no impact on other request paths.

The fix is correct: multi_items() returns a list in Starlette so iterating it twice (once for files, once for non-file fields) is safe, and httpx accepts both list-of-tuples for files and dict-of-lists for data. Single-value non-file fields now arrive as ["value"] instead of "value", which httpx encodes identically. All previous tests are updated to match the new shapes, and the new regression test directly exercises the duplicate-key scenario without any assertion-collapsing workarounds.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/pass_through_endpoints/pass_through_endpoints.py Switches multipart form parsing from form.items() to form.multi_items(), emits files as a list of tuples, and groups repeated non-file fields into list values — correctly forwarding all parts under duplicate field names.
tests/test_litellm/proxy/pass_through_endpoints/test_pass_through_endpoints.py Updates existing multipart tests to use FormData([...]) and assert on the list-of-tuples shape; adds a dedicated regression test covering repeated file and non-file fields under the same key.

Reviews (2): Last reviewed commit: "refactor(passthrough): address multipart..." | Re-trigger Greptile

Comment thread litellm/proxy/pass_through_endpoints/pass_through_endpoints.py Outdated
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Build form_data_dict in one pass with groupby instead of rescanning form_items per field name, and assert on the files list directly in the boundary regression test so repeated field names are not collapsed by dict().

Co-authored-by: Cursor <[email protected]>
@shivamrawat1

Copy link
Copy Markdown
Collaborator Author

@greptile review again with new commit that resolves the p2 issues

@yuneng-berri yuneng-berri merged commit e4aedb0 into litellm_internal_staging Jun 27, 2026
122 checks passed
@yuneng-berri yuneng-berri deleted the litellm_multipart_file_upload branch June 27, 2026 02:54
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.

3 participants