Skip to content

fix(agent): recover 'not a multimodal model' image rejections from text-only endpoints (port from block/buzz#5318) - #85005

Open
teknium1 wants to merge 1 commit into
mainfrom
buzz-port/image-rejection-multimodal-phrase
Open

teknium1 wants to merge 1 commit into
mainfrom
buzz-port/image-rejection-multimodal-phrase

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Summary

Text-only serving endpoints (Crusoe serverless, vLLM deployments of text-only checkpoints, other OpenAI-compatible hosts) reject any request whose history contains an image with HTTP 400 "<model-id> is not a multimodal model" — and that phrasing was missing from _IMAGE_REJECTION_PHRASES, so the image stayed in history, every retry failed identically, and the session was permanently poisoned. This PR adds the phrase so the existing strip-images-and-retry fallback fires.

Ported from block/buzz#5318, where Block hit this exact failure live: their classifier only matched OpenRouter's 404 wording, the Crusoe 400 fell through to terminal, and 8 benchmark trials wedged with 12.7h aggregate idle-after-poison before they widened the match. Same bug shape exists in our agent/conversation_loop.py phrase gate — verified by feeding the Crusoe body through the current tuple (no match on main; matches with this PR).

Ours vs theirs

Buzz Hermes
Recovery machinery replace_unsupported_images() strips + placeholders + continues turn _strip_images_from_messages() + session-wide _vision_supported=False + retry — already present
Gap Classifier matched only OpenRouter 404 wording Phrase gate missing the not a multimodal model wording
Fix Widen is_unsupported_image_input_error() Add phrase to _IMAGE_REJECTION_PHRASES (this PR)

Their PR's second half (unbounding benchmark agent rounds) is bench-harness-specific and not ported.

Changes

  • agent/conversation_loop.py: add "not a multimodal model" to _IMAGE_REJECTION_PHRASES (matches "is not a multimodal model" and bare variants; the existing 4xx status gate still applies)
  • tests/run_agent/test_image_rejection_fallback.py: positive test for the Crusoe/vLLM body; negative test that _MULTIMODAL_TOOL_CONTENT_PATTERNS shapes ("tool content must be a string", "text is not set") do NOT trip this gate — they have their own cheaper recovery

Validation

Before After
"crusoeai/GLM-5.2-NVFP4 is not a multimodal model" no phrase match → terminal retry loop, session poisoned strips images, retries text-only
image-too-large / tool-content-string shapes not matched still not matched (tests)
tests/run_agent/test_image_rejection_fallback.py 5 passed 7 passed
test_multimodal_tool_content_recovery.py + test_error_classifier.py 84 passed

Infographic

Session poison fixed — strip images, retry text-only

…al model' rejections

Text-only serving endpoints (Crusoe serverless, vLLM text-only
checkpoints) reject any request whose history contains an image with
HTTP 400 '<model-id> is not a multimodal model'. That phrasing was
missing from _IMAGE_REJECTION_PHRASES, so the images stayed in history,
every retry failed identically, and the session was permanently
poisoned. block/buzz hit this exact failure live (block/buzz#5318:
8 wedged benchmark trials, 12.7h aggregate idle time) and widened
their classifier; this ports the same phrase coverage to our
strip-images-and-retry fallback.
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on da67b21 — Port from block/buzz#5318: recover text-only endpoint 'not a

⚠️ Warnings

OSV vulnerability scan · View job

3 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 7m50s vs 7m (+11.9%). 6 job(s) slower, 15 faster, 3 unchanged.

  • Python tests / Run tests slice 7/12: -25.0s
  • Python tests / Run tests slice 1/12: -25.0s
  • Python tests / Run tests slice 2/12: -20.0s
  • Python tests / Run tests slice 9/12: +15.0s
  • Python tests / Run tests slice 11/12: +12.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/vision Vision analysis and image generation labels Aug 13, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(agent): recover 'not a multimodal model' image rejections from text-only endpoints (port from block/buzz#5318)

  1. agent/conversation_loop.py:4112 — the new phrase "not a multimodal model" is matched as a plain substring of the lowercased error body. Unlike the other phrases in the list (which describe the request's images: "image_url", "support image input"), this phrase can also appear when the user asks the text-only model to produce images or when a provider explains a capability restriction that has nothing to do with images in history. Stripping images and retrying then wastes a full retry and can mask the real error. Consider scoping the match to errors that also reference image input (e.g. require "image" within the same error text), or accepting the wasted-retry cost deliberately with a comment.

  2. tests/run_agent/test_image_rejection_fallback.py — the new positive tests cover the Crusoe/vLLM wording, but there is no false-positive case where "not a multimodal model" appears without any image involvement (e.g. "text-to-image generation is not supported; model is not a multimodal model"). Given the existing test_anthropic_image_too_large_does_not_trip precedent for guarding false positives, adding one would pin the intended scope of the phrase.

@Sora-bluesky

Copy link
Copy Markdown
Contributor

Cross-reference: #69104 (open, CI green) handles an adjacent xAI wording of image-history poisoning — HTTP 400 "Invalid PNG image." / "Base64 string of provided image cannot be decoded." — as FailoverReason.image_corrupt: a payload-only image strip and retry that leaves vision on, distinct from the _IMAGE_REJECTION_PHRASES path here that switches vision off for the session. Tests live in tests/run_agent/test_69078_image_corrupt_recovery.py. Happy to rebase #69104 on top of this PR once it lands, or to have the xAI wordings folded in here if that is simpler for the maintainers.

This branch has not been deployed

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

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists tool/vision Vision analysis and image generation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants