TL;DR
- Error:
No tool output found for tool call call_01_...(HTTP 400,invalid_request_error) in Codex CLI / Codex Desktop with DeepSeek or another strict OpenAI-compatible provider.- Cause: Codex batched two
view_imagecalls. After resizing a large image, it inserted an<image_resize_notice>developer message between the twofunction_call_outputitems. DeepSeek's Responses API expects each output to directly follow its call, so it rejected the request.- Why it never recovers: the broken turn stays in the thread history, so every later message fails with the same call ID.
- Workaround: make
view_imagethe only tool call in its turn, and pre-shrink screenshots (viewport-only, ≤1500 px wide). Start a new thread to recover.- Ready-made rules: I packaged all of this as an agent skill for Codex and Claude Code: https://github.com/Kar23232/unstuck-qa
The error
If you're here, you've probably seen this in Codex:
No tool output found for tool call call_01_HcX4jELNhOOdX0zqGayn0609.
It usually appears right after the agent says something like "Viewed 2 images". After that, every message you send returns the exact same error.
How I ran into it
I use Codex Desktop with DeepSeek as the model provider (model_provider = "deepseek", wire_api = "responses"). I was doing QA work: rendering document pages to PNG to check the layout, and screenshotting a web app to reproduce bugs.
Mid-task, the agent looked at two images at once. Then:
No tool output found for tool call call_01_UISHMq0wlN4CLWt3E6Wa2694.
I typed "?". Same error. I typed something much less polite. Same error, same call ID. The thread was dead.
It happened in three separate threads: twice on September 17 and once on September 21, 2026.
My first guess, and the first explanation an AI assistant gave me, was that the provider was dropping image outputs, or that the image was too big. The image was only 91 KB, so size seemed ruled out. Both guesses were wrong.
Reading the rollout logs
Codex saves every thread as JSONL under:
~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
Search for the failing call ID to find the right file:
grep -rl "call_01_HcX4" ~/.codex/sessions
Here's the timeline around the failure (timestamps in seconds, image payloads removed):
26.172 function_call view_image call_00_… page-1.png
26.350 function_call view_image call_01_… page-2.png
26.376 function_call_output call_00_…
26.377 message (developer) <image_resize_notice> …
26.382 function_call_output call_01_…
26.382 message (developer) <image_resize_notice> …
26.745 ❌ 400 No tool output found for tool call call_01_…
Three things stood out, and they were identical in all three dead threads:
-
Both outputs exist. Nothing was lost.
call_01has itsfunction_call_output. -
A developer message sits between them. Codex inserted an
<image_resize_notice>right after output #1, so it landed between output #1 and output #2. -
It always fails on the second call (
call_01_…), never the first.
About the 91 KB: resizing depends on pixel dimensions, not file size. A small PNG of a tall rendered page can still go over the limit. CodexPlusPlus#2275 mentions images over roughly 2048 px or about 2.5 million pixels.
Root cause: strict adjacency meets an inserted notice
In the Responses API, a conversation is a list of items: function_call, function_call_output, message, reasoning, and so on.
-
OpenAI's Responses API pairs a
function_callwith itsfunction_call_outputbycall_id, wherever they sit in the list (as described in deepseek-ai/awesome-deepseek-integration#740). - DeepSeek's Responses-compatible endpoint requires the output to come directly after its call. Issue #740 reproduces this cleanly: put a developer message or a reasoning item between a call and its output and you get the 400; keep them adjacent and you get 200.
Now add Codex's resize behavior. When an image in a tool output gets resized, Codex appends an <image_resize_notice> developer message after that output.
✅ One image: fine ❌ Two images in parallel: rejected
function_call A function_call A
function_call_output A function_call B
developer: resize notice function_call_output A
developer: resize notice ← breaks adjacency
function_call_output B ← "No tool output found"
With one image, the notice comes after the output and nothing breaks. With two parallel images, the notice for image A lands between the two outputs, and DeepSeek decides call_01 was never answered.
Why the thread is wedged forever: Codex resends the full history on every turn, so the same malformed sequence goes out every time and gets the same 400. Resuming the thread replays the same history, so it fails too.
A second path to the same error
The resize notice isn't the only trigger. Other people have documented related paths:
-
openai/codex#44604: the next request is sent before the last tool output of a batch is appended. Reported when
view_imageis batched with more images (about a 70 ms gap) or with a command that exceeds the 30-second tool wait. -
farion1231/cc-switch#7569: the proxy's media-lift feature inserts a synthetic user message mid-batch when
view_imageruns in parallel withexec_commandor MCP tools. -
BigPizzaV3/CodexPlusPlus#2275: when translating to Chat Completions, the resize notice ends up between
toolmessages, and the upstream rejects the request withMessages with role 'tool' must be a response to a preceding message with 'tool_calls'.
The common thread: an image sharing a batch with other tool calls, plus a provider that enforces strict ordering.
Are you hitting this? Quick checklist
- ✔️ Codex CLI or Codex Desktop with DeepSeek, or another OpenAI-compatible provider or proxy
- ✔️ The failing ID looks like
call_01_…(the second call of a batch) - ✔️ It happened right after "Viewed 2 images", or after
view_imageran alongside another tool - ✔️ Every later message fails with the same call ID
If most of these match, it's very likely this bug.
Workarounds
1. Make images go alone. view_image should be the only tool call in its turn: never two images at once, and never an image batched with a command. You can put this in AGENTS.md:
- Never call view_image in parallel with any other tool call. One image per turn.
- Before viewing an image, downscale it to ≤1500 px wide. Use viewport screenshots, not full-page captures.
2. Shrink images before the agent looks at them, so Codex never needs to insert a resize notice:
# macOS (limits the longest side to 1500 px)
sips -s format jpeg -Z 1500 shot.png --out shot.jpg
# ImageMagick (only shrinks if wider than 1500 px)
magick shot.png -resize '1500x>' shot.jpg
For very tall pages, take several viewport screenshots instead of one full-page capture.
3. Recover a wedged thread. Start a new thread; retrying and resuming both replay the broken history. To avoid losing work, have the agent keep a progress.md that records what it has finished, so the new thread can continue from there. A more surgical option (described in cc-switch#7569) is to delete the orphaned call/output items from the rollout JSONL. Back the file up first, since this is unofficial.
4. Or switch providers. According to #740, OpenAI's Responses API pairs by call_id, so this ordering shouldn't break there. I haven't tested that myself.
I turned this into a skill: unstuck-qa 🔓
After losing three threads, I wrote the rules down as an agent skill: unstuck-qa. It works in both Codex and Claude Code.
On top of the two rules above, it covers the other ways an agent gets stuck while testing web apps in a real browser:
- 💬 Native dialogs: registers a
confirm()/alert()handler before clicking, so clicks don't hang forever - ⏱️ No
networkidlewaits: waits for specific elements with timeouts, which matters on pages that poll in the background - 💾 One click, one verdict for save actions: no endless "refresh and try again" loops
- 📒 A progress log, so a dead thread doesn't mean starting over
- 🧪 Skip the UI when possible: unit or API checks take about 10× fewer tool calls than clicking around
Install:
# Codex
git clone https://github.com/Kar23232/unstuck-qa ~/.codex/skills/unstuck-qa
# Claude Code
git clone https://github.com/Kar23232/unstuck-qa ~/.claude/skills/unstuck-qa
What would fix it properly
- Codex: emit resize notices after the whole batch of outputs, and validate call/output pairing right before sending (see openai/codex#46193).
- Proxies (cc-switch, CodexPlusPlus and similar): reorder items so every output directly follows its call before forwarding.
-
DeepSeek: pair calls and outputs by
call_id, the same way OpenAI's Responses API does (#740).
Related issues
- openai/codex#44604: next request sent before the last tool output is appended
- openai/codex#46193: pairing only enforced when history is rebuilt, not at send time
- deepseek-ai/awesome-deepseek-integration#740: DeepSeek requires adjacent call/output
-
farion1231/cc-switch#7190:
image_resize_noticeinserted between tool results -
farion1231/cc-switch#7569: parallel
view_image+ other tools -
BigPizzaV3/CodexPlusPlus#2275: resize notice breaks
toolmessage order
Wrapping up
If your Codex thread keeps answering every message with No tool output found for tool call, nothing is wrong with your files. A notice got wedged between two tool results, and the thread will never recover on its own. Start fresh, keep images alone, shrink them first, and you shouldn't see it again.
If this saved your afternoon, a ⭐ on unstuck-qa helps the next person find it. Found another trigger? Open an issue there.
Top comments (0)