Skip to content

fix(fireworks): skip #transform=inline for base64 data URLs#23729

Merged
5 commits merged intoBerriAI:litellm_oss_staging_03_17_2026from
awais786:fix/fireworks-base64-image-padding
Mar 17, 2026
Merged

fix(fireworks): skip #transform=inline for base64 data URLs#23729
5 commits merged intoBerriAI:litellm_oss_staging_03_17_2026from
awais786:fix/fireworks-base64-image-padding

Conversation

@awais786
Copy link
Copy Markdown
Contributor

Closes #23583

Appending #transform=inline to a data: URL corrupted the base64 payload, causing binascii.Error (Incorrect padding) when Fireworks AI attempted to decode the image. Data URLs are already inlined so the fragment is a no-op anyway — guard both the str and dict image_url branches to skip the suffix when the URL starts with "data:".

Relevant issues

Pre-Submission checklist

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

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test

Changes

Closes BerriAI#23583

Appending #transform=inline to a data: URL corrupted the base64 payload,
causing binascii.Error (Incorrect padding) when Fireworks AI attempted to
decode the image. Data URLs are already inlined so the fragment is a no-op
anyway — guard both the str and dict image_url branches to skip the suffix
when the URL starts with "data:".

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Mar 17, 2026 5:30am

Request Review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR fixes a base64 corruption bug in the Fireworks AI image transformation path: #transform=inline was unconditionally appended to every image URL, but appending a fragment to a data: URL corrupts the base64 payload (the # character is not a valid base64 character), causing a binascii.Error: Incorrect padding on the Fireworks side.

Changes:

  • litellm/llms/fireworks_ai/chat/transformation.py: Both the str and dict image_url branches in _add_transform_inline_image_block now check url.lower().startswith("data:") before appending #transform=inline. The .lower() call correctly handles the case-insensitive URI scheme per RFC 3986, and is validated with a dedicated test case (Data:image/...).
  • tests/llm_translation/test_fireworks_ai_translation.py: Three new parametrized cases cover the str branch, the dict branch, and the mixed-case scheme variant.
  • tests/test_litellm/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py: A new standalone unit test (test_add_transform_inline_image_block_skips_data_urls) exercises all three scenarios using no real network calls, fully compliant with the mock-only test policy for this directory.

Confidence Score: 5/5

  • This PR is safe to merge — the fix is minimal, isolated, and well-tested with no side-effects on existing behaviour.
  • The change is a two-line guard added to an existing utility method. It correctly handles both the str and dict URL branches, uses a case-insensitive prefix check per RFC 3986, and is covered by tests in both test directories. No pre-existing tests were modified, no backwards-incompatible changes were made, and no network calls are introduced in the new tests.
  • No files require special attention — only a trivial PEP 8 blank-line omission in the new test file.

Important Files Changed

Filename Overview
litellm/llms/fireworks_ai/chat/transformation.py Correct and minimal fix — guards both the str and dict image_url branches so that data: URLs never receive the #transform=inline fragment; includes a case-insensitive .lower() check per RFC 3986.
tests/llm_translation/test_fireworks_ai_translation.py Three new parametrized test cases added: str branch, dict branch, and case-insensitive (Data:) variant — good coverage of the fix.
tests/test_litellm/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py Standalone regression test with correct mock-only calls; minor PEP 8 formatting issue — missing two blank lines before the subsequent @pytest.mark.parametrize decorator.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[_add_transform_inline_image_block called] --> B{vision in model OR disable flag?}
    B -- Yes --> C[Return content unchanged]
    B -- No --> D{image_url is str?}
    D -- Yes --> E{starts with data: ?}
    E -- Yes --> F[Return content unchanged - data URL already inlined]
    E -- No --> G[Append transform=inline to image_url str]
    G --> H[Return content]
    D -- No --> I{image_url is dict?}
    I -- Yes --> J{url starts with data: ?}
    J -- Yes --> K[Return content unchanged - data URL already inlined]
    J -- No --> L[Append transform=inline to image_url.url]
    L --> H
    I -- No --> H
Loading

Last reviewed commit: 37e1885

Comment on lines 187 to +196
if isinstance(content["image_url"], str):
content["image_url"] = f"{content['image_url']}#transform=inline"
# Skip base64 data URLs — appending #transform=inline corrupts the
# base64 payload and causes an "Incorrect padding" decode error on
# the Fireworks side. Data URLs are already inlined by definition.
if not content["image_url"].startswith("data:"):
content["image_url"] = f"{content['image_url']}#transform=inline"
elif isinstance(content["image_url"], dict):
content["image_url"][
"url"
] = f"{content['image_url']['url']}#transform=inline"
url = content["image_url"]["url"]
if not url.startswith("data:"):
content["image_url"]["url"] = f"{url}#transform=inline"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test cases for the data: URL fix

The fix adds guards for both the str and dict branches, but no test cases were added to validate this behaviour. The existing test_transform_inline parametrized test in tests/llm_translation/test_fireworks_ai_translation.py covers HTTP URLs and vision-model bypass, but has no coverage for data: URLs (the exact scenario described in the linked issue).

The pre-submission checklist also explicitly requires at least one test in tests/test_litellm/. Without it, a future refactor could reintroduce the regression silently. The following cases are missing:

  • str branch: {"image_url": "data:image/png;base64,abc123"} → URL should remain unchanged
  • dict branch: {"image_url": {"url": "data:image/jpeg;base64,xyz=="}} → URL should remain unchanged

Example additions to the test_transform_inline parametrize list:

(
    {"image_url": "data:image/png;base64,iVBORw0KGgo="},
    "gpt-4",
    "data:image/png;base64,iVBORw0KGgo=",  # must not be modified
),
(
    {"image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ=="}},
    "gpt-4",
    {"url": "data:image/jpeg;base64,/9j/4AAQ=="},  # must not be modified
),

Rule Used: What: Ensure that any PR claiming to fix an issue ... (source)

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq bot commented Mar 16, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing awais786:fix/fireworks-base64-image-padding (27894d4) with main (58e74a6)

Open in CodSpeed

Comment on lines +124 to +135
result = config._add_transform_inline_image_block(str_content)
assert result["image_url"] == data_url, "data URL must not be modified (str branch)"

# dict branch
dict_content = {"type": "image_url", "image_url": {"url": data_url}}
result = config._add_transform_inline_image_block(dict_content)
assert result["image_url"]["url"] == data_url, "data URL must not be modified (dict branch)"

# regular https URL should still get the suffix
https_content = {"type": "image_url", "image_url": "https://example.com/image.jpg"}
result = config._add_transform_inline_image_block(https_content)
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test calls will raise TypeError — missing required arguments

_add_transform_inline_image_block has two required positional parameters (model: str and disable_add_transform_inline_image_block: Optional[bool]) that have no default values. All three invocations in this test omit both arguments, so every call will immediately raise:

TypeError: _add_transform_inline_image_block() missing 2 required positional arguments: 'model' and 'disable_add_transform_inline_image_block'

The tests need to pass a model name that does not contain "vision" (so the early-return guard is not triggered) and False for the disable flag:

Suggested change
result = config._add_transform_inline_image_block(str_content)
assert result["image_url"] == data_url, "data URL must not be modified (str branch)"
# dict branch
dict_content = {"type": "image_url", "image_url": {"url": data_url}}
result = config._add_transform_inline_image_block(dict_content)
assert result["image_url"]["url"] == data_url, "data URL must not be modified (dict branch)"
# regular https URL should still get the suffix
https_content = {"type": "image_url", "image_url": "https://example.com/image.jpg"}
result = config._add_transform_inline_image_block(https_content)
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"
result = config._add_transform_inline_image_block(str_content, model="fireworks_ai/llama-v3", disable_add_transform_inline_image_block=False)
assert result["image_url"] == data_url, "data URL must not be modified (str branch)"
# dict branch
dict_content = {"type": "image_url", "image_url": {"url": data_url}}
result = config._add_transform_inline_image_block(dict_content, model="fireworks_ai/llama-v3", disable_add_transform_inline_image_block=False)
assert result["image_url"]["url"] == data_url, "data URL must not be modified (dict branch)"
# regular https URL should still get the suffix
https_content = {"type": "image_url", "image_url": "https://example.com/image.jpg"}
result = config._add_transform_inline_image_block(https_content, model="fireworks_ai/llama-v3", disable_add_transform_inline_image_block=False)
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"

Comment on lines +191 to +196
if not content["image_url"].startswith("data:"):
content["image_url"] = f"{content['image_url']}#transform=inline"
elif isinstance(content["image_url"], dict):
content["image_url"][
"url"
] = f"{content['image_url']['url']}#transform=inline"
url = content["image_url"]["url"]
if not url.startswith("data:"):
content["image_url"]["url"] = f"{url}#transform=inline"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Case-insensitive data: scheme check

Per RFC 3986, URI schemes are case-insensitive, so a data: URL could theoretically be written as Data:image/... or DATA:image/... and bypass this guard. While virtually all real-world data URLs use lowercase, a defensive lowercase normalisation would make this airtight:

Suggested change
if not content["image_url"].startswith("data:"):
content["image_url"] = f"{content['image_url']}#transform=inline"
elif isinstance(content["image_url"], dict):
content["image_url"][
"url"
] = f"{content['image_url']['url']}#transform=inline"
url = content["image_url"]["url"]
if not url.startswith("data:"):
content["image_url"]["url"] = f"{url}#transform=inline"
if not content["image_url"].lower().startswith("data:"):
content["image_url"] = f"{content['image_url']}#transform=inline"
elif isinstance(content["image_url"], dict):
url = content["image_url"]["url"]
if not url.lower().startswith("data:"):
content["image_url"]["url"] = f"{url}#transform=inline"

@awais786 awais786 force-pushed the fix/fireworks-base64-image-padding branch from 27894d4 to 6a3a2d9 Compare March 16, 2026 11:10
@ghost ghost changed the base branch from main to litellm_oss_staging_03_17_2026 March 17, 2026 05:27
@ghost ghost merged commit 84b4af4 into BerriAI:litellm_oss_staging_03_17_2026 Mar 17, 2026
5 checks passed
Comment on lines +141 to 142
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"
@pytest.mark.parametrize(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing blank lines between test functions

PEP 8 requires two blank lines between top-level definitions. There is no blank line between the closing assert of test_add_transform_inline_image_block_skips_data_urls and the @pytest.mark.parametrize decorator that begins the next test. While this does not affect test execution, it makes the file harder to read and will typically trigger linting warnings.

Suggested change
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"
@pytest.mark.parametrize(
assert result["image_url"].endswith("#transform=inline"), "https URL should get #transform=inline"
@pytest.mark.parametrize(

ghost pushed a commit that referenced this pull request Mar 21, 2026
fix(fireworks): skip #transform=inline for base64 data URLs (#23729)
This pull request was closed.
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]: Fireworks Image Issue

1 participant