Repository navigation
fix(images): stop forwarding the raw image[] and mask[] form keys - #39512
Conversation
The /v1/images/edits handler binds the documented image[] and mask[] aliases into their canonical parameters, then re-reads the multipart body, so the raw bracketed keys rode along to the provider next to the values already built from them. OpenAI rejected both: image[] as "Invalid type for 'image[0]'" and mask[] as "Invalid parameter: 'mask'". Drop both aliases from what gets forwarded.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bugbot run |
Pull request was closed
…keys Forkit-Contribution: BerriAI#39512 Forkit-Source-Base: 066d5f0 Forkit-Source-Head: 06fb9dc Forkit-Applied-To: v1.100.1 Forkit-Input: sha256:2da47ca821e6ef88631df9078e810e9d298587ed05ac8d3574026aa7ebf389e8
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
|
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Can confirm this fixes the issue. Also works with Open WebUI's image edit feature |
…ts_bracketed_alias
|
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 255190c. Configure here.
fix(images): backport the image[] and mask[] form key drop to rc/1.102.0 (#39512)
|
Do we know which version will include this fix? 🙏 |
…2.0 (BerriAI#39512) (cherry picked from commit 289c52b)
TLDR
Problem this solves:
/v1/images/editsforwarded the rawimage[]key to providersmask[]400s the same way: "This parameter already has a different value"How it solves it:
image[]andmask[]from what gets forwardedimageandmaskUser Flow
Before: a developer following LiteLLM's own documented
image[]form gets a 400 and no edited imagePOST https://litellm-domain/v1/images/editswithmodel=gpt-image-1,image[]=@tree.png, andprompt=add a small red hat on top, exactly the form the endpoint's own docs showInvalid type for 'image[0]': expected a file, but got a string instead.image=@tree.png, which returns HTTP 200 with an edited imageimage=@tree.pngplusmask[]=@mask.png, and get back HTTP 400:Invalid parameter: 'mask'. This parameter already has a different value in your parameters.mask=@mask.png, which returns HTTP 200, so the documented bracketed form is unusable through the gateway on either fieldAfter: the same documented
image[]andmask[]requests come back with an edited imagePOST https://litellm-domain/v1/images/editswithmodel=gpt-image-1,image[]=@tree.png, andprompt=add a small red hat on top, exactly the form the endpoint's own docs showb64_jsonimage and ausageblockimage=@tree.png, which returns HTTP 200 with an edited imageimage=@tree.pngplusmask[]=@mask.png, and get back HTTP 200 with an edited imagemask=@mask.pnginstead and get HTTP 200 as well, so both spellings work on both fieldsimageandimage[]together still returns HTTP 422Cannot specify both 'image' and 'image[]', so the existing guard against ambiguous requests is unchangedRelevant issues
Fixes #41421
Affected release
regression in v1.100.0 (PR #38104), still present in v1.101.0 and v1.102.0-rc.2
Linear ticket
Resolves LIT-6816
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
Both legs run the same proxy config against the real OpenAI API with
gpt-image-2.5-sunburst, the model the reporting customer hit this on, two uvicorn workers (--num_workers 2) on each proxy, and the same 256x256tree.png/tree2.png/tree3.png/mask.pngset. The customer's own call shape is the OpenAI Python SDK'sclient.images.edit(image=[...]), which the SDK sends as repeatedimage[]parts, so the SDK cases come first and the curl cases cover the documented bracketed forms directly. Real spend on every 200. Before is the merge basebc9f4fec5b(current main), After is this PR's tip255190ca35, same nine cases in the same order on bothconfig.yaml:sdk_edit.py:Each case is the command followed by its output. A 200 is summarized to
created,usage.total_tokens, theb64_jsonlength, and the number of images returned; an error shows the proxy's response bodyBefore (bc9f4fe)
SDK: three images in a list (the customer flow)
SDK: a list of one image
SDK: a single image, not a list
curl: two image[] parts
curl: one image[] part
curl: canonical image part
curl: image plus mask[]
curl: image plus canonical mask
curl: image and image[] in one request (rejected on both sides)
After (255190c)
SDK: three images in a list (the customer flow)
SDK: a list of one image
SDK: a single image, not a list
curl: two image[] parts
curl: one image[] part
curl: canonical image part
curl: image plus mask[]
curl: image plus canonical mask
curl: image and image[] in one request (rejected on both sides)
Observations from the run:
mask[]400s too on Before, different OpenAI error; fixed hereimageplusimage[]still 422s on both sides; unchangedimage[]parts yield one edited image, 744 tokens; provider behaviorType
🐛 Bug Fix
Caveats (if any)
Low
llm_translation_testing,litellm_utils_testing,ocr_testingimage[]/mask[]keysimage[0]parts still 500 on both sidesFinal Attestation
Note
Low Risk
Scoped bug fix to proxy image-edit request shaping with new regression tests; no auth or billing changes.
Overview
Fixes a regression where
/v1/images/editsstill forwarded raw multipart keysimage[]andmask[]to upstream providers after the handler had already normalized those uploads intoimage/mask, which caused OpenAI (and SDK clients using bracketed parts) to return 400s.The image edit handler now defines shared constants for those aliases and strips
image[]andmask[]from the coerced formdatabefore routing, while continuing to populateimageandmaskfrom the file parameters. Tests assert bracketed aliases are omitted from the forwarded payload, canonicalimage/maskuploads are unchanged, and existing multipart numeric coercion behavior is preserved.Reviewed by Cursor Bugbot for commit 255190c. Bugbot is set up for automated code reviews on this repo. Configure here.