Repository navigation
Conversation
…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]>
|
👋 Hi aryanputta! Thank you for contributing to ai-dynamo/dynamo. Just a reminder: The 🚀 |
WalkthroughThis PR fixes bug ChangesUnsupported Image Format Error Handling
🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
components/src/dynamo/common/tests/multimodal/test_image_loader.py (1)
243-243: 💤 Low valueMove
import base64to module top.The
import base64is function-local; hoist it to the top-level imports for consistency.♻️ Proposed change
@@ test file top imports import asyncio +import base64 from io import BytesIOasync 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
📒 Files selected for processing (2)
components/src/dynamo/common/multimodal/image_loader.pycomponents/src/dynamo/common/tests/multimodal/test_image_loader.py
|
/ok to test de87cd6 |
|
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? |
|
Hi @rmccorm4, thanks for the review. Both points are addressed in 7c8c072. The open comment was correct. The 415 only reached direct Fix: track the first On local validation: yes. I ran the full |
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]>
7c8c072 to
3726441
Compare
|
/ok to test 3726441 |
krishung5
left a comment
There was a problem hiding this comment.
Thanks for the contribution!
| 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 |
There was a problem hiding this comment.
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.
…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]>
…of 500 (#10281) Signed-off-by: Aryan <[email protected]>
…of 500 (ai-dynamo#10281) Signed-off-by: Aryan <[email protected]> Signed-off-by: shenls <[email protected]>
…of 500 (ai-dynamo#10281) Signed-off-by: Aryan <[email protected]> Signed-off-by: shenls <[email protected]>
Summary
When a client submits an image in an unsupported format (e.g. SVG),
ImageLoaderwas returning HTTP 500 instead of the semantically correct 415 Unsupported Media Type.Root cause:
Image.open(..., formats=["JPEG", "PNG", "WEBP"])raisesPIL.UnidentifiedImageErrorfor any unrecognized format. This was caught by the genericexcept Exceptionhandler in_fetch_and_processand re-wrapped asValueError, causing the frontend to return 500.Fix: Catch
Image.UnidentifiedImageErrorexplicitly (and the belt-and-suspendersValueError("Unsupported image format: ...")from_open_image_sync) in both code paths — the HTTP/HTTPS path in_fetch_and_processand thedata:URL branch inload_image— and raiseHttpStatusError(415, "Unsupported Media Type", url)instead. The existingHttpStatusErrorpassthrough in the frontend delivers the correct 415 to the client.Files changed
components/src/dynamo/common/multimodal/image_loader.py— two exception handlers patchedcomponents/src/dynamo/common/tests/multimodal/test_image_loader.py— two new unit testsTest plan
test_unsupported_format_url_raises_415— SVG served at an HTTP URL raisesHttpStatusError(415)test_unsupported_format_data_url_raises_415— SVG in adata:URL raisesHttpStatusError(415)test_image_loader.pytests still passgpu_0(CPU-only, no GPU required)Fixes #10263
Summary by CodeRabbit
Release Notes
Bug Fixes