openclaw - 💡(How to fix) Fix Image content blocks stripped between session storage and provider request (bifrost-routed anthropic-messages)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

  1. The anthropic-messages API for this provider is dispatched through a different transport function not on this code path, or
  2. The image block is stripped earlier in the pipeline (lossless-claw assembly, runtime context engine, transport message sanitization) before reaching the convert step, and the keepImages filter is then a no-op because the blocks are already gone.

Fix Action

Workaround

Until this lands I’m setting agents.defaults.imageModel to bifrost-claude/claude-opus-4-7 explicitly (same as the active chat model), which forces a separate image-understanding pass that does work. The auto-route to active-model is broken; the explicit-route is fine.

Happy to gather more diagnostic data if there’s a specific log/trace you want — just point me at the code path that actually handles outbound provider requests for this config.

Code Example

{
  "type": "image",
  "data": "/9j/2wBDAA...",
  "mimeType": "image/jpeg"
}

---

{
    "baseUrl": "http://10.2.10.10:4000/anthropic",
    "api": "anthropic-messages",
    "timeoutSeconds": 120
  }

---

require("node:fs").appendFileSync("/tmp/img-debug.log", "[CONV-CALLED] " + ...);
RAW_BUFFERClick to expand / collapse

Inline image content blocks in user messages are dropped before the request body reaches the upstream provider. Verified with full diagnostic evidence.

Symptom

Sending an image attached to a Slack message reaches the OpenClaw gateway with a properly-shaped image block in the session jsonl:

{
  "type": "image",
  "data": "/9j/2wBDAA...",
  "mimeType": "image/jpeg"
}

But the request the gateway POSTs to the provider contains zero image blocks. The model receives text only and either confabulates or asks what was sent.

Environment

  • OpenClaw 2026.5.19 (a185ca2) on macOS Darwin 24.3.0 arm64
  • Node v26.0.0
  • Provider: bifrost-claudeclaude-opus-4-7 (Bifrost gateway forwarding to Anthropic)
  • Provider config:
    {
      "baseUrl": "http://10.2.10.10:4000/anthropic",
      "api": "anthropic-messages",
      "timeoutSeconds": 120
    }
  • Model catalog: "input": ["text", "image"] on claude-opus-4-7
  • agents.defaults.imageModel: unset at time of repro (so the auto-route “use active model if it supports vision” should kick in)

Repro

  1. Configure bifrost-claude provider pointing at any Anthropic-compatible upstream.
  2. Set agents.defaults.imageModel to undefined so the auto-route engages.
  3. Send a Slack message with a PNG attachment to a session bound to bifrost-claude/claude-opus-4-7.
  4. Inspect:
    • Session JSONL: image block has the expected {type, data, mimeType} shape.
    • Provider raw request (e.g. Bifrost logs.raw_request): zero image blocks anywhere in messages[].content[]. content_summary is text-only.
  5. Model replies with a confabulated description (or "I cant see an image").

Diagnostic instrumentation that failed to fire

dist/provider-stream-CTEZu_RC.js::convertAnthropicMessages and dist/openai-transport-stream-fYhjd00v.js::transformTransportMessages were patched with unconditional file-append calls at function entry:

require("node:fs").appendFileSync("/tmp/img-debug.log", "[CONV-CALLED] " + ...);

After gateway restart and triggering an image-bearing request that the model did answer (so the request flow completed end-to-end), /tmp/img-debug.log was never created. This suggests either:

  1. The anthropic-messages API for this provider is dispatched through a different transport function not on this code path, or
  2. The image block is stripped earlier in the pipeline (lossless-claw assembly, runtime context engine, transport message sanitization) before reaching the convert step, and the keepImages filter is then a no-op because the blocks are already gone.

I tried tracing through the lossless-claw assemble-debug logs and media-understanding subsystem — both show the image arriving at the gateway (image resize log fires, session jsonl has it) but never anything image-shaped getting handed to the provider transport.

Existing strip locations I checked

  • dist/provider-stream-CTEZu_RC.js: filters by model.input.includes("image") — would only strip if input array was wrong. Confirmed input is ["text", "image"].
  • dist/transport-stream-DyvRcab3.js: gemini-style, only applies to tool-result images.
  • dist/thread-lifecycle-UmGQWfNc.js::sanitizeImageContentRecord: validates data URLs, would not silently strip a valid base64 JPEG.
  • dist/context-engine-maintenance-DrkvercJ.js::isImageContent: type guard only.

None of these account for the disappearance.

Bifrost-side proof

Bifrost log entries for claude-opus-4-7 in this session:

  • raw_request length: 590–675 KB (so the body is being captured)
  • Image blocks in messages[].content[]: 0
  • content_summary: text-only, no [image] markers

This rules out Bifrost stripping on its end — the gateway is sending text-only.

Asks

  1. Confirm which transport function actually handles bifrost-claude requests (or any api: anthropic-messages provider with non-Anthropic upstream). The dispatch from createAnthropicMessagesTransportStreamFn is presumably the entry, but it’s not being reached based on the instrumentation.
  2. Add a transport-level diagnostic event that emits the input-block-type breakdown of each outbound request (text/image/toolCall counts and provider). This is the kind of thing that would have made this 5 minutes instead of 6 hours.
  3. Document the contract for agents.defaults.imageModel: the "auto-route to active model when it has image input" behaviour is non-obvious and the slot’s description ("Optional image model (provider/model) used when the primary model lacks image input") implies it’s only a fallback, but resolveAutoEntries short-circuits on it before checking the active model. That tripped me up: I cleared imageModel expecting auto-route to kick in, but images still didn’t reach the active model.

Workaround

Until this lands I’m setting agents.defaults.imageModel to bifrost-claude/claude-opus-4-7 explicitly (same as the active chat model), which forces a separate image-understanding pass that does work. The auto-route to active-model is broken; the explicit-route is fine.

Happy to gather more diagnostic data if there’s a specific log/trace you want — just point me at the code path that actually handles outbound provider requests for this config.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Image content blocks stripped between session storage and provider request (bifrost-routed anthropic-messages)