Skip to content

fix(frontend): return HTTP 415 for unsupported image formats instead of 500 - #10281

Merged
krishung5 merged 2 commits into
ai-dynamo:mainfrom
aryanputta:fix/image-loader-415-unsupported-media-type
Jun 4, 2026
Merged

krishung5 merged 2 commits into
ai-dynamo:mainfrom
aryanputta:fix/image-loader-415-unsupported-media-type

Conversation

@aryanputta

@aryanputta aryanputta commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When a client submits an image in an unsupported format (e.g. SVG), ImageLoader was returning HTTP 500 instead of the semantically correct 415 Unsupported Media Type.

Root cause: Image.open(..., formats=["JPEG", "PNG", "WEBP"]) raises PIL.UnidentifiedImageError for any unrecognized format. This was caught by the generic except Exception handler in _fetch_and_process and re-wrapped as ValueError, causing the frontend to return 500.

Fix: Catch Image.UnidentifiedImageError explicitly (and the belt-and-suspenders ValueError("Unsupported image format: ...") from _open_image_sync) in both code paths — the HTTP/HTTPS path in _fetch_and_process and the data: URL branch in load_image — and raise HttpStatusError(415, "Unsupported Media Type", url) instead. The existing HttpStatusError passthrough in the frontend delivers the correct 415 to the client.

Files changed

  • components/src/dynamo/common/multimodal/image_loader.py — two exception handlers patched
  • components/src/dynamo/common/tests/multimodal/test_image_loader.py — two new unit tests

Test plan

  • test_unsupported_format_url_raises_415 — SVG served at an HTTP URL raises HttpStatusError(415)
  • test_unsupported_format_data_url_raises_415 — SVG in a data: URL raises HttpStatusError(415)
  • All existing test_image_loader.py tests still pass
  • Tests are gpu_0 (CPU-only, no GPU required)

Fixes #10263


Open in Devin Review

Summary by CodeRabbit

Release Notes

Bug Fixes

  • Enhanced error handling for unsupported image formats. When encountering incompatible image types (such as SVG files), the system now provides a clearer "Unsupported Media Type" error message instead of a generic error. This applies to images loaded from URLs and embedded data URLs, improving user experience when dealing with format incompatibilities.

…of 500

When a client submits an image in an unsupported format (e.g. SVG),
PIL raises UnidentifiedImageError, which was previously swallowed by
the generic except-Exception handler and re-wrapped as ValueError,
causing the frontend to return HTTP 500 (Internal Server Error).

Catch Image.UnidentifiedImageError and the belt-and-suspenders
ValueError("Unsupported image format: ...") from _open_image_sync
explicitly in both _fetch_and_process (HTTP/HTTPS path) and the
data: URL branch of load_image, and raise HttpStatusError(415, ...)
instead. The existing HttpStatusError passthrough in the frontend
then delivers the semantically correct 415 Unsupported Media Type.

Add two unit tests covering the HTTP URL path and the data: URL path.

Fixes ai-dynamo#10263

Signed-off-by: Aryan <[email protected]>
@aryanputta
aryanputta requested review from a team as code owners June 3, 2026 19:54
@copy-pr-bot

copy-pr-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

👋 Hi aryanputta! Thank you for contributing to ai-dynamo/dynamo.

Just a reminder: The NVIDIA Test Github Validation CI runs an essential subset of the testing framework to quickly catch errors.Your PR reviewers may elect to test the changes comprehensively before approving your changes.

🚀

@github-actions github-actions Bot added fix external-contribution Pull request is from an external contributor multimodal labels Jun 3, 2026
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR fixes bug #10263 by adding explicit HTTP 415 error handling when the ImageLoader encounters unsupported image formats. Previously, unsupported formats like SVG would result in generic HTTP 500 errors; now they correctly return HTTP 415 (Unsupported Media Type) with appropriate error messages.

Changes

Unsupported Image Format Error Handling

Layer / File(s) Summary
Error handling for unsupported image formats
components/src/dynamo/common/multimodal/image_loader.py
_fetch_and_process() (lines 143–151) and load_image() data URL decode branch (lines 220–226) now catch PIL Image.UnidentifiedImageError and ValueError cases indicating "Unsupported image format", converting them to HttpStatusError(415, "Unsupported Media Type", image_url).
Test coverage for unsupported image formats
components/src/dynamo/common/tests/multimodal/test_image_loader.py
Helper _make_svg_bytes() (lines 227–228) generates minimal SVG bytes. Two new async tests validate that loading SVG via both URL-based fetch (lines 231–239) and base64-encoded data URL (lines 241–249) raises HttpStatusError with status 415.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: returning HTTP 415 instead of 500 for unsupported image formats, which matches the core objective of the PR.
Description check ✅ Passed The description provides a comprehensive overview, root cause analysis, the fix, affected files, and a test plan aligned with the PR template sections.
Linked Issues check ✅ Passed The PR fully addresses issue #10263 by implementing proper exception handling to return HTTP 415 for unsupported image formats instead of 500 in both URL and data: URL code paths.
Out of Scope Changes check ✅ Passed All changes are within scope: the image_loader.py modifications directly fix the error handling for unsupported formats, and test_image_loader.py additions provide test coverage for the fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
components/src/dynamo/common/tests/multimodal/test_image_loader.py (1)

243-243: 💤 Low value

Move import base64 to module top.

The import base64 is function-local; hoist it to the top-level imports for consistency.

♻️ Proposed change
@@ test file top imports
 import asyncio
+import base64
 from io import BytesIO
 async def test_unsupported_format_data_url_raises_415(loader: ImageLoader) -> None:
     """A data: URL carrying an SVG payload should raise HttpStatusError 415."""
-    import base64
-
     svg_b64 = base64.b64encode(_make_svg_bytes()).decode()

As per coding guidelines: "Keep imports at the top of the file (flag any function-level imports)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/src/dynamo/common/tests/multimodal/test_image_loader.py` at line
243, Hoist the function-local "import base64" to the module-level imports in
test_image_loader.py: remove the local import from inside the function and add
"import base64" with the other top-of-file imports so base64 is imported once at
module load time; update any function that referenced the local import to use
the top-level symbol (no other code changes needed).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@components/src/dynamo/common/tests/multimodal/test_image_loader.py`:
- Line 243: Hoist the function-local "import base64" to the module-level imports
in test_image_loader.py: remove the local import from inside the function and
add "import base64" with the other top-of-file imports so base64 is imported
once at module load time; update any function that referenced the local import
to use the top-level symbol (no other code changes needed).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a67f6b74-76d6-4b1a-b69f-4e7c571a51fd

📥 Commits

Reviewing files that changed from the base of the PR and between 32ab8da and de87cd6.

📒 Files selected for processing (2)
  • components/src/dynamo/common/multimodal/image_loader.py
  • components/src/dynamo/common/tests/multimodal/test_image_loader.py

@rmccorm4
rmccorm4 requested a review from furionw June 4, 2026 21:11
@rmccorm4

rmccorm4 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

/ok to test de87cd6

@rmccorm4

rmccorm4 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Hi @aryanputta, thanks for the contribution! Please address the open comment here: #10281 (comment)

Were you able to locally validate this change works as expected?

@rmccorm4
rmccorm4 requested a review from krishung5 June 4, 2026 21:13
@aryanputta

Copy link
Copy Markdown
Contributor Author

Hi @rmccorm4, thanks for the review. Both points are addressed in 7c8c072.

The open comment was correct. The 415 only reached direct load_image() callers. load_image_batch() gathered results with return_exceptions=True and re-raised a single plain Exception, which stripped the HttpStatusError status. Since the frontend drives the batch path, an unsupported format still surfaced as a 500.

Fix: track the first HttpStatusError during aggregation and re-raise it so the status survives. All other failures keep the existing collective Exception path. I also hoisted the function-local import base64 in the test module per CodeRabbit.

On local validation: yes. I ran the full test_image_loader.py suite locally against the modified module, 15/15 pass including the two new load_image_batch tests (URL and data: URL, both asserting 415). I also checked the regression directly: with the old aggregation a batch SVG raises a generic Exception (status stripped, so 500), and with the fix it raises HttpStatusError(415). ruff and black are clean on both files.

The 415 Unsupported Media Type fix only reached direct load_image()
callers. load_image_batch() gathered every failure with
return_exceptions=True and re-raised a single plain Exception, stripping
the HttpStatusError status. Since the frontend drives the batch path, an
unsupported format (e.g. SVG) still surfaced as a 500.

Track the first HttpStatusError during aggregation and re-raise it so the
status survives, falling back to the existing collective Exception for all
other failures. Add load_image_batch SVG tests for both the URL and data:
URL paths asserting status 415.

Also hoist the function-local `import base64` in the test module to the
top-level imports per the CodeRabbit nitpick.

Signed-off-by: Aryan <[email protected]>
@aryanputta
aryanputta force-pushed the fix/image-loader-415-unsupported-media-type branch from 7c8c072 to 3726441 Compare June 4, 2026 21:33
@KrishnanPrash

Copy link
Copy Markdown
Contributor

/ok to test 3726441

@krishung5 krishung5 left a comment

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.

Thanks for the contribution!

@krishung5
krishung5 merged commit 857ad3a into ai-dynamo:main Jun 4, 2026
98 of 99 checks passed
raise
except Image.UnidentifiedImageError as e:
logger.error(f"Unsupported image format loading: '{image_url}'")
raise HttpStatusError(415, "Unsupported Media Type", image_url) from e

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.

Raising dynamo.common.http.HttpStatusError preserves .status inside ImageLoader, but the normal backend path converts unknown Python exceptions to a DynamoError string and the Rust OpenAI layer only extracts non-500 codes from JSON error payloads, so this still reaches clients as HTTP 500. Fix: convert HttpStatusError into the backend/frontend error format that Rust parses before it leaves Python.

🤖 AI Fix

Update lib/bindings/python/rust/backend.rs::py_err_to_dynamo and lib/bindings/python/rust/engine.rs::process_item to detect Python exceptions exposing status or code plus message, serialize the DynamoError message as {"message": <message>, "code": <status/code>} so lib/llm/src/http/service/openai.rs::extract_backend_error_if_present returns 415, then add a frontend/backend test where ImageLoader.load_image_batch() raises HttpStatusError(415, ...) and the OpenAI response status is 415.

krishung5 added a commit that referenced this pull request Jun 5, 2026
…ndary

Detect `.code`/`.status` + `.message` on Python exceptions and emit a
JSON-shaped DynamoError message so the OpenAI frontend recovers the HTTP
status instead of defaulting to 500. Required for #10281's 415-from-SVG
fix to actually reach the client.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
tmonty12 pushed a commit that referenced this pull request Jun 8, 2026
Broduker pushed a commit to Broduker/dynamo that referenced this pull request Jun 12, 2026

This branch had an error being deployed

1 failed deployment
GITLAB — 37264410 Deployed Jun 4, 2026 by copy-pr-bot[bot] via Trigger CI Pipeline #30259
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contribution Pull request is from an external contributor fix multimodal size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Use error 415 when processing images instead of 500.

5 participants