Skip to content

fix: normalize unsupported images before sending to vision providers - #9780

Open
unknowbug wants to merge 3 commits into
AstrBotDevs:masterfrom
unknowbug:fix/unsupported-image-format
Open

fix: normalize unsupported images before sending to vision providers#9780
unknowbug wants to merge 3 commits into
AstrBotDevs:masterfrom
unknowbug:fix/unsupported-image-format

Conversation

@unknowbug

@unknowbug unknowbug commented Aug 22, 2026

Copy link
Copy Markdown

Fixes #9771

Summary

OpenAI-compatible vision providers such as DeepSeek reject image formats outside webp/png/jpeg/gif with a 400 unsupported image error. AstrBot currently:

  • preserves the original unsupported image_url block when local image materialization fails (return resolved_part or part);
  • injects tool-returned cached images using their raw MIME type (including image/svg+xml, image/bmp, etc.) without any provider-safe conversion;
  • does not recognize "unsupported image" in _handle_api_error, so a transient bad image leaves the whole agent in ERROR instead of falling back to text-only.

This PR normalizes unsupported image formats before they are sent to the provider, guards the tool cached-image review path, and adds the missing error fallback.

Changes

  • media_utils: add normalize_image_for_provider():
    • keeps webp/png/jpeg/gif as-is;
    • corrects mislabeled MIME when the actual bytes are already provider-safe;
    • converts BMP/TIFF/AVIF etc. to JPEG (or PNG when transparency is present);
    • returns None for images that cannot be parsed (e.g. SVG) so callers can omit them.
  • openai_source:
    • _resolve_image_part() now normalizes image data before constructing image_url;
    • _transform_content_part() replaces an unusable image with a text placeholder instead of keeping the bad image_url;
    • _is_invalid_attachment_error() now matches "unsupported image", enabling the existing text-only retry fallback.
  • tool_loop_agent_runner:
    • cached tool images are normalized before being added to the LLM review context;
    • unsupported/corrupt images are skipped (the textual [Image from tool ...] marker remains).
  • Tests:
    • test_media_utils.py: normalize_image_for_provider BMP conversion, unsupported SVG handling, supported MIME passthrough.
    • test_openai_source.py: unsupported-image API error fallback, BMP to JPEG resolution, SVG rejection, unresolvable context image placeholder replacement.

Notes

tool_image_cache is intentionally left unchanged: cached images may still need to be delivered to users in their original format (e.g. SVG). The provider-review boundary is now guarded by normalize_image_for_provider().

Summary by Sourcery

Normalize image content at the provider boundary and gracefully retry image failures without visual inputs.

Bug Fixes:

  • Normalize unsupported and mislabeled images before sending them to vision providers, converting compatible formats and omitting unparseable images.
  • Fall back to text-only requests when providers reject images as unsupported or when image-bearing requests exceed the payload limit.
  • Replace unresolved context images with a text placeholder instead of forwarding the original invalid image block.

Enhancements:

  • Guard tool-image review context by normalizing cached images and skipping images that cannot be converted while retaining their textual markers.

Tests:

  • Add media normalization coverage for format conversion, MIME correction, supported-image passthrough, and invalid image handling.
  • Add provider tests covering image conversion, unsupported-image omission, placeholder replacement, and text-only fallbacks.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Aug 22, 2026

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/utils/media_utils.py" line_range="164" />
<code_context>
+
+    supported = supported_mimes or IMAGE_PROVIDER_SUPPORTED_MIME_TYPES
+    mime = (getattr(image_data, "mime_type", "") or "").lower()
+    if mime in supported:
+        return image_data
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Images whose declared MIME type is already in the provider-supported set are returned without decoding or validating their bytes. A corrupt payload or an unsupported image mislabeled as `image/png`, `image/jpeg`, `image/webp`, or `image/gif` is therefore still sent to the provider and triggers the same unsupported/invalid-image request failure this normalization is intended to prevent.

**Triggers:** When cached tool data or an image reference carries a provider-safe MIME label but contains invalid or differently formatted bytes.

**Suggested fix:** Validate the payload even for supported MIME types, and return the original data only when the detected format matches a supported provider format; otherwise convert it or return `None`.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: astrbot/core/utils/media_utils.py:164


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/core/utils/media_utils.py Outdated

@sourcery-ai sourcery-ai 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.

Sourcery assessment

Approved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] DeepSeek 视觉模型在工具循环中因不支持的图片格式报 400 unsupported image

1 participant