[Bug] Built-in Devin provider drops images inside Claude tool_result before building the upstream request
Is it a request payload issue?
- Yes — specific request content is silently lost during conversion; the request does not necessarily return an error.
- No
Describe the bug
When using CPA's built-in Devin provider through POST /v1/messages, images nested inside Claude tool_result.content reach CPA but do not appear in the Devin request object. This affects screenshots returned by computer-use tools.
The tool-call ID survives, so CPA still constructs a tool-result prompt and continues the request. Its image payload is silently discarded, leaving the model without the screenshot needed for the next step.
CLI Type
CPA's built-in Devin provider, using the Connect-RPC GetChatMessage endpoint at server.codeium.com.
Model Name
Client model: devin/swe-2. The logged upstream selector is swe-2-medium; incoming output_config.effort is medium.
LLM Client
ZCode. The request User-Agent identifies ZCode/3.11.2; the tool results come from its computer-use screenshot/zoom tools.
OS Type / Environment
- Server: Linux, x86_64.
- Client: macOS.
- CLIProxyAPI:
v7.3.5, commitb681a1e0f7b89d26814f788b60bf84feaf72e912. - Endpoint:
POST /v1/messages, streaming. - Observation date: September 17, 2026, UTC+08:00.
Request Information
Abbreviated request structure, with tool IDs, text and image data replaced:
{
"model": "devin/swe-2",
"stream": true,
"max_tokens": 128000,
"thinking": {"type": "enabled", "budget_tokens": 8000},
"output_config": {"effort": "medium"},
"tool_choice": {"type": "auto"},
"tools": [{
"name": "screenshot",
"description": "Capture a screenshot",
"input_schema": {"type": "object", "properties": {}}
}],
"messages": [
{"role": "user", "content": "Inspect the screenshot returned by the tool."},
{"role": "assistant", "content": [
{"type": "tool_use", "id": "tool_image_1", "name": "screenshot", "input": {}}
]},
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "tool_image_1", "content": [
{"type": "image", "source": {
"type": "base64", "media_type": "image/png", "data": "<BASE64_PNG>"
}}
]}
]}
]
}
Observed behavior
In an observed request, the incoming conversation contained four images inside tool_result.content. The corresponding DEVIN UPSTREAM REQUEST log contained zero images across all prompts.
Expected behavior
Valid supported images returned by tools should survive request conversion and reach the Devin request with the correct tool-call association.
How the bug occurs
The affected input is an image nested inside a tool result, rather than a top-level user image:
messages[].content[]
└─ tool_result (tool_use_id)
└─ content[]
└─ image (source.type=base64, source.media_type, source.data)
The request goes through three stages:
Claude tool_result containing an image
→ Interactions function_result: image removed, call_id retained
→ Devin tool-result prompt: Content and ToolCallID set, Images empty
→ Original-request image supplement: nested tool image not visited
1. The tool-result translator keeps only text
claudeToolResultToInteractions converts the Claude block to an Interactions function_result. When content is an array, it appends only items whose type is text; there is no branch preserving image items.
For the image-only tool result illustrated above, this code produces the following shape (the tool name is resolved from the preceding call):
{
"type": "function_result",
"call_id": "tool_image_1",
"name": "screenshot",
"result": []
}
The Base64 data and MIME type are gone at this step. For a mixed text/image result, the text items survive but the images are dropped. The function still returns a tool-result entry; it does not report the discarded content as an error.
2. The Devin consumer has no tool-image extraction
The function_result branch in parseInteractionsPayload reads the result as text/raw JSON and constructs:
helps.DevinPrompt{
MessageID: uuid.New().String(),
Source: 4,
ToolCallID: id,
Content: resText,
}
Source: 4 represents a tool result. This branch reads the result into Content; it does not extract image parts or populate Images.
3. The original-request fallback does not recover nested images
supplementImagesFromOriginal can recover images from the original request, but it only scans direct image parts in user-message content. At that level the block here is tool_result, so its nested content is never inspected. The helper also attaches recovered images only to Source: 1 user prompts, not Source: 4 tool-result prompts.
This is why the existing user-image handling does not cover screenshots returned by tools. The tool-result entry and its ID remain in the conversation, while its image data never reaches the constructed Devin prompt.
Source: router-for-me/CLIProxyAPI