Preflight Checklist
Related but not identical, cross-referenced deliberately: #91648 (same defect shape on Windows/Bash), #83848 (subagent transcripts stopping after a tool_use with no tool_result), #92789 (idle-not-crashed session hang on Linux desktop). I believe these share a root cause. What I add here is sample evidence showing the event loop is idle with nothing registered, plus a case where a parent and its in-process teammate wedged independently in the same OS process.
What's Wrong?
A Read tool call's tool_result is never delivered. The turn's continuation never settles, and the process goes fully idle rather than erroring or timing out. The elapsed-time counter keeps incrementing, so it is indistinguishable from slowness.
This happened twice within one OS process: first in a background in-process teammate, then in the parent session 4.5 minutes later.
The process is idle, not deadlocked and not spinning. sample of the wedged process, taken twice ~5 minutes apart, identical in shape both times:
3213 Thread_32466316 DispatchQueue_1: com.apple.main-thread (serial)
! : 3148 kevent64 (in libsystem_kernel.dylib) + 8
3148 of 3213 samples on the main thread are in kevent64, the idle event-loop wait. Every Bun Pool worker thread is parked in __ulock_wait2. CPU steady at ~1%. No thread is blocked in a file or socket syscall and no mutex is contended on the main thread. Nothing is registered that could ever wake the turn, which is why waiting cannot recover it.
No API request is in flight. lsof -nP -i -a -p <pid> shows zero sockets in ESTABLISHED.
Exactly one orphaned tool_use per agent. Counting tool_use ids against tool_result ids in the transcripts:
| Agent |
tool_use |
tool_result |
orphaned |
| Parent session |
71 |
70 |
1 |
| In-process teammate |
8 |
7 |
1 |
Both orphans are Read, both targeting small local files on APFS:
parent 2026-09-14T09:31:30.991Z Read <26,618-byte local .md>
teammate 2026-09-14T09:26:58.200Z Read <27,445-byte local .md>
isApiErrorMessage entries in either transcript: 0.
Out-of-order completion in the teammate. It emitted two Read calls in one assistant message, 203ms apart. The second returned in 15ms; the first never returned:
09:26:58.200 assistant tool_use:Read(ORPHAN) <- never returns
09:26:58.403 assistant tool_use:Read(SIBLING)
09:26:58.418 user tool_result(SIBLING) <- returns in 15ms
That is the signature of a lost result in parallel tool-call fan-in.
Teammate metadata (from the subagent .meta.json):
{"taskKind": "in_process_teammate", "requestShape": "background",
"requestNonInteractive": true, "permissionMode": "auto", "spawnDepth": 0}
The teammate wedged first; the parent wedged on its next tool call 4.5 minutes later. Since an in-process teammate shares the parent's event loop, a dropped continuation in one branch appears able to strand the other.
What Should Happen?
A tool_use with no tool_result should hit a timeout and surface a tool error, letting the turn continue or fail visibly. It should never leave the session in a state where the event loop is idle with no pending work and no path to recovery.
Error Messages/Logs
There are none, and that is the core of the report. No error in the UI, no error in the transcript, no timeout, no retry. The only way to distinguish this from ordinary slowness is to sample the process and notice the main thread is in the idle kevent64 wait with nothing registered.
Secondary symptom: pressing Esc returns to the prompt, but subsequent prompts also produce no output, which suggests the agent loop's state does not fully reset after the cancel.
Steps to Reproduce
I do not have a deterministic repro, so I am reporting the conditions and the measurement method instead.
Conditions present in both occurrences:
- A long-running interactive session (~127.7k tokens of context at the time of the hang).
- One background in-process teammate spawned via the Agent tool, running concurrently in the same OS process.
- A
Read on a small local file, dispatched while that teammate was live.
To detect it rather than guess at it:
sample <pid> 5 on the apparently-stuck process. If the main thread is in kevent64 and the worker threads are in __ulock_wait2, it is idle, not busy.
lsof -nP -i -a -p <pid> and confirm zero ESTABLISHED sockets, so no request is in flight.
- In the session
.jsonl, collect every tool_use id and every tool_result tool_use_id and diff them. The orphan is the dropped call.
Claude Model
claude-opus-5 (parent), claude-sonnet-5 (in-process teammate)
Is this a regression?
I don't know
Last Working Version
(not known)
Claude Code Version
2.1.270 (Claude Code)
Platform
AWS Bedrock
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
macOS 27.0 (build 26A428), Darwin 27.0.0, xnu-13432.1.9, arm64 (T6041). Runtime is Bun; worker threads appear as Bun Pool N.
Ruled out by testing, please do not treat these as the cause:
- File or filesystem. Both target files read instantly from another session afterward. Local APFS, not a File Provider or network mount. No stuck
fileproviderctl, no processes in U state.
- File size. In the same session a 356,726-byte PDF and a 315,945-byte PDF read successfully, and a 25,777-byte markdown read successfully twice. The orphans were 26,618 and 27,445 bytes. No size threshold.
- Permissions. An
allow rule covered both paths and auto mode was on. 70 of 71 calls in the same session succeeded, including a read in the same directory as the teammate's orphan.
- Host resource exhaustion. 63% memory free, 2.31MB of 1024MB swap used, 1303 of 8000 processes for the uid.
- Hung hook or child process. The only children were
caffeinate and two idle MCP servers.
- Socket state. The three
CLOSED FDs are ordinary HTTP-pool residue; healthy sessions on the same machine had 14 and 5.
Suggested fix direction: a watchdog on pending tool_use ids. If a tool_use has no tool_result after N seconds and the event loop has nothing pending, surface a tool error instead of waiting forever. That alone converts a silent unrecoverable hang into a recoverable error, independent of whatever drops the result.
Update (after two further failure modes were found): this fix direction is too narrow. Two other paths reach the same idle event loop with no pending tool_use at all -- #94261 (compaction stalls permanently at 95%) and #94335 (the tool_result IS delivered and the turn still never continues). A watchdog keyed on outstanding tool calls catches only this issue. The formulation that covers all three is "a turn is marked in progress while the event loop has no registered work for N seconds". Note also that this failure is invisible from the parent side when it happens in a background agent: the Agent tool's tool_result is the immediate "Spawned successfully" string, so the parent's pending-call bookkeeping always balances no matter what happens to the agent afterwards.
Impact: the session is unrecoverable in place and queued user messages are stranded, though they remain readable in the transcript as queue-operation/enqueue records and --resume restores state through the last complete turn.
Paths, hostnames, repository names, session ids and tool-use ids in this report are replaced with placeholders. Byte sizes, timestamps and counts are unmodified.
Preflight Checklist
latest)Related but not identical, cross-referenced deliberately: #91648 (same defect shape on Windows/Bash), #83848 (subagent transcripts stopping after a
tool_usewith notool_result), #92789 (idle-not-crashed session hang on Linux desktop). I believe these share a root cause. What I add here issampleevidence showing the event loop is idle with nothing registered, plus a case where a parent and its in-process teammate wedged independently in the same OS process.What's Wrong?
A
Readtool call'stool_resultis never delivered. The turn's continuation never settles, and the process goes fully idle rather than erroring or timing out. The elapsed-time counter keeps incrementing, so it is indistinguishable from slowness.This happened twice within one OS process: first in a background in-process teammate, then in the parent session 4.5 minutes later.
The process is idle, not deadlocked and not spinning.
sampleof the wedged process, taken twice ~5 minutes apart, identical in shape both times:3148 of 3213 samples on the main thread are in
kevent64, the idle event-loop wait. EveryBun Poolworker thread is parked in__ulock_wait2. CPU steady at ~1%. No thread is blocked in a file or socket syscall and no mutex is contended on the main thread. Nothing is registered that could ever wake the turn, which is why waiting cannot recover it.No API request is in flight.
lsof -nP -i -a -p <pid>shows zero sockets inESTABLISHED.Exactly one orphaned
tool_useper agent. Countingtool_useids againsttool_resultids in the transcripts:tool_usetool_resultBoth orphans are
Read, both targeting small local files on APFS:isApiErrorMessageentries in either transcript: 0.Out-of-order completion in the teammate. It emitted two
Readcalls in one assistant message, 203ms apart. The second returned in 15ms; the first never returned:That is the signature of a lost result in parallel tool-call fan-in.
Teammate metadata (from the subagent
.meta.json):{"taskKind": "in_process_teammate", "requestShape": "background", "requestNonInteractive": true, "permissionMode": "auto", "spawnDepth": 0}The teammate wedged first; the parent wedged on its next tool call 4.5 minutes later. Since an in-process teammate shares the parent's event loop, a dropped continuation in one branch appears able to strand the other.
What Should Happen?
A
tool_usewith notool_resultshould hit a timeout and surface a tool error, letting the turn continue or fail visibly. It should never leave the session in a state where the event loop is idle with no pending work and no path to recovery.Error Messages/Logs
There are none, and that is the core of the report. No error in the UI, no error in the transcript, no timeout, no retry. The only way to distinguish this from ordinary slowness is to
samplethe process and notice the main thread is in the idlekevent64wait with nothing registered.Secondary symptom: pressing Esc returns to the prompt, but subsequent prompts also produce no output, which suggests the agent loop's state does not fully reset after the cancel.
Steps to Reproduce
I do not have a deterministic repro, so I am reporting the conditions and the measurement method instead.
Conditions present in both occurrences:
Readon a small local file, dispatched while that teammate was live.To detect it rather than guess at it:
sample <pid> 5on the apparently-stuck process. If the main thread is inkevent64and the worker threads are in__ulock_wait2, it is idle, not busy.lsof -nP -i -a -p <pid>and confirm zeroESTABLISHEDsockets, so no request is in flight..jsonl, collect everytool_useid and everytool_resulttool_use_idand diff them. The orphan is the dropped call.Claude Model
claude-opus-5(parent),claude-sonnet-5(in-process teammate)Is this a regression?
I don't know
Last Working Version
(not known)
Claude Code Version
2.1.270 (Claude Code)
Platform
AWS Bedrock
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
macOS 27.0 (build 26A428), Darwin 27.0.0, xnu-13432.1.9, arm64 (T6041). Runtime is Bun; worker threads appear as
Bun Pool N.Ruled out by testing, please do not treat these as the cause:
fileproviderctl, no processes inUstate.allowrule covered both paths and auto mode was on. 70 of 71 calls in the same session succeeded, including a read in the same directory as the teammate's orphan.caffeinateand two idle MCP servers.CLOSEDFDs are ordinary HTTP-pool residue; healthy sessions on the same machine had 14 and 5.Suggested fix direction: a watchdog on pending
tool_useids. If atool_usehas notool_resultafter N seconds and the event loop has nothing pending, surface a tool error instead of waiting forever. That alone converts a silent unrecoverable hang into a recoverable error, independent of whatever drops the result.Update (after two further failure modes were found): this fix direction is too narrow. Two other paths reach the same idle event loop with no pending
tool_useat all -- #94261 (compaction stalls permanently at 95%) and #94335 (thetool_resultIS delivered and the turn still never continues). A watchdog keyed on outstanding tool calls catches only this issue. The formulation that covers all three is "a turn is marked in progress while the event loop has no registered work for N seconds". Note also that this failure is invisible from the parent side when it happens in a background agent: theAgenttool'stool_resultis the immediate "Spawned successfully" string, so the parent's pending-call bookkeeping always balances no matter what happens to the agent afterwards.Impact: the session is unrecoverable in place and queued user messages are stranded, though they remain readable in the transcript as
queue-operation/enqueuerecords and--resumerestores state through the last complete turn.Paths, hostnames, repository names, session ids and tool-use ids in this report are replaced with placeholders. Byte sizes, timestamps and counts are unmodified.