Codex Desktop can remain in Thinking after successful tool calls with no streamed follow-up or a hung /responses request
Summary
Codex Desktop can remain in the UI Thinking state after tool calls have already completed successfully. After inspecting local session JSONL and core SQLite logs across several affected turns, the visible Thinking state appears to cover multiple distinct backend states:
- A stale turn state after a successful tool result, with no further session events emitted until the user interrupts.
- A follow-up
/responsesrequest that is started after successful tool results but then produces no streamed output for several minutes, until the user interrupts. - Context compaction after tool output, which can look similar in the UI but does eventually resume.
- A long-running model request after tool output that eventually returns, also shown only as
Thinking.
This makes it hard to tell whether the model is actually reasoning, the backend request is hung, compaction is running, or the turn runner is stale.
Environment
Sanitized environment from affected runs:
- Product: Codex Desktop
- Client version observed in logs:
26.519.81530 - Core/app version observed in telemetry:
0.133.0 - OS: macOS, arm64
- Model:
gpt-5.5 - Reasoning effort:
xhigh - Approval policy:
never - Sandbox: full local access
- Auth mode: ChatGPT
Account identifiers, email addresses, exact local paths, thread IDs, and prompt contents have been intentionally omitted.
Observed Patterns
Case A: tool success followed by no session activity until interrupt
In one affected turn, the last tool call was an apply_patch that completed successfully. After the tool result was written, there were no further session JSONL events until the user interrupted several minutes later.
Sanitized event sequence:
T+0s custom_tool_call_output: apply_patch success
T+0s token_count
T+216s user interrupt
T+216s turn_aborted
During that interval there were no new session entries of these types:
reasoning
message
function_call
function_call_output
token_count
task_complete
This suggests the UI was still showing Thinking, but there was no visible evidence that the model was actively reasoning or that any new tool work was pending.
Case B: tool success followed by a follow-up /responses request that hangs
In another affected turn, multiple shell tools completed successfully. The runner then decided a model follow-up was required and started a new /responses request. No streamed output was produced for over eight minutes, until the user interrupted.
Sanitized session sequence:
T+0s function_call_output: success
T+0s function_call_output: success
T+0s function_call_output: success
T+0s token_count
T+492.7s user interrupt
T+492.7s turn_aborted
Relevant sanitized core log signal:
post sampling token usage
total_usage_tokens=117208
estimated_token_count=Some(96568)
auto_compact_scope_limit=244800
token_limit_reached=false
model_needs_follow_up=true
has_pending_input=false
needs_follow_up=true
stream_request: POST /backend-api/codex/responses
There was no Request completed log and no streamed reasoning, message, function_call, or token_count event before the interrupt.
This suggests this instance was not local tool execution and not visible local reasoning; it was a pending follow-up model request that did not produce output or a failure event for several minutes.
Case C: tool success followed by context compaction
A separate affected-looking interval was actually context compaction. In that case, tool output completed, then a context_compacted event appeared around 30 seconds later, and reasoning resumed shortly after.
Sanitized sequence:
T+0s function_call_output: success
T+0s token_count
T+~30s context_compacted
T+~35s reasoning resumes
This is less concerning functionally, but the UI still only showed Thinking, making it indistinguishable from the stuck cases above.
Case D: long follow-up request that eventually returns
Another case showed a post-tool follow-up /responses request taking around 149 seconds before returning. It eventually emitted reasoning, so it was not permanently stuck, but it looked identical to the hung case in the UI while waiting.
Sanitized signal:
POST /backend-api/codex/responses
Request completed: status=200, duration_ms=149510
reasoning emitted afterward
Expected Behavior
After tool calls complete successfully, Codex Desktop should avoid silently staying in Thinking indefinitely.
Ideally:
- If a follow-up model request is pending, the UI should distinguish "waiting for model response" from "reasoning".
- If a follow-up
/responsesstream produces no events for a long time, Codex should emit a timeout, retry, or visible recoverable error. - If the runner has
needs_follow_up=truebut no progress occurs, there should be watchdog logging and a task-level failure or recovery path. - If context compaction is running, the UI should identify that state separately.
- If no model request is pending and no events are emitted after tool success, the turn should not remain open without
task_complete,turn_aborted, or an error event.
Why this matters
From the user's perspective, all of these cases look like the same Thinking state after tools have already succeeded. The only practical workaround is to interrupt the turn and continue, but interruption can lose momentum and may leave turns in confusing states.
Agent-level instructions such as "after tool success, promptly respond" do not fix the hung cases because the model does not regain control while the follow-up request is pending or the runner is stale.
Potentially Related Issues
This may overlap with or provide more granular diagnostics for:
- #24336
- #21360
- #24571
- #24618
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗