[BUG] Stale extension process resolves parked permission requests hours later, re-parenting the session tip onto an abandoned branch — resume then hides the real conversation
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
If tool calls are left pending permission approval when a VSCode window closes, and the session is resumed in another window, a stale extension host process can later flush error tool_results for those long-abandoned tool calls to the end of the session .jsonl, parented onto the abandoned branch of the conversation tree.
Because resume reconstruction picks the tip by walking parentUuid from the last user/assistant record in file order, every subsequent resume then displays the abandoned branch: in my case ~1.5 hours of conversation (~300 records) became invisible in the UI and the LLM context, and the session appeared to end hours earlier at a failed-Edit message. To the user this looks like the tail of the session was erased — while the data is fully intact in the file the whole time.
Timeline observed (record types only, timestamps relative):
- T0 — assistant issues 4
Edittool_use records; permission prompts shown; the VSCode window is closed without resolving them. Session shutdown metadata is written. - T0+4min — user resumes in a new window. Resume forks past the 4 unresolved tool_use records from the last text message (expected behavior). Conversation continues ~1.5h to a normal conclusion.
- T0+3.5h — a stale extension process appends 4
usertool_result records answering the T0 tool_use ids (error results, see below) plus an attachment chain — all parented onto the abandoned branch. - Every resume from then on reconstructs from those error records and hides the real dialog.
This is the same tip-selection weakness as #33651 and #43764 (both closed), but with a new trigger — parked permission requests + a stale process — and it hits plain single-agent sessions: no subagents, no power loss. Anchors that might disambiguate are ignored: appending an attachment record parented to the good branch's tip, or a last-prompt record with leafUuid pointing at it, changes nothing.
Workaround (verified): reordering the genuine records — moving the late error tool_results + their attachment chain from EOF to just after their parent tool_use records, so the real final assistant message is again the last user/assistant record — fully restores the session. Tree edges untouched, nothing added or removed.
What Should Happen?
- Results flushed for parked/stale permission requests should not be able to move the resume tip onto a branch that a later fork already abandoned (drop them, or mark them
isSidechainso they can't participate in tip selection). - More robustly: persist an explicit tip pointer on shutdown and prefer it on resume, instead of deriving the tip purely from file order — so one late write to a dead branch can't hijack the whole session view.
Error Messages/Logs
The late-flushed tool_result records contain:
Tool permission request failed: AbortError: Tool permission stream closed before response received
and for the remaining pending tool calls:
<tool_use_error>File does not exist. ...</tool_use_error>
The reconstruction rule is confirmed by the binary's own diagnostic string:
Conversation reconstruction walks parentUuid from the last record, so unlinked records are dropped — the file's producer must chain records (parentUuid null on the first, the previous record's uuid on each subsequent one).
Steps to Reproduce
Live reproduction (racy — depends on a stale extension host):
- In the VSCode extension, get Claude to issue several file-Edit tool calls that require permission approval; do not answer the prompts.
- Close the VSCode window (or drop the remote connection) with the prompts pending.
- Reconnect, resume the same session, and continue the conversation for a while (resume forks past the unresolved tool_use records — expected).
- When the stale extension host from step 2 finally terminates, it appends error tool_results for the step-1 tool_use ids onto the abandoned branch.
- Close and resume the session again → it now displays the abandoned branch ending in the failed Edits; everything from step 3 is invisible.
Deterministic reproduction of the tip-selection behavior (minimal session file; boilerplate fields trimmed for readability — I can provide a complete loadable example on request):
{"type":"user", "uuid":"A","parentUuid":null,"message":{"role":"user","content":"hi"}}
{"type":"assistant","uuid":"B","parentUuid":"A","message":{"role":"assistant","content":[{"type":"text","text":"ok"}]}}
{"type":"assistant","uuid":"C","parentUuid":"B","message":{"role":"assistant","content":[{"type":"tool_use","id":"t1","name":"Edit","input":{}}]}}
{"type":"user", "uuid":"D","parentUuid":"B","message":{"role":"user","content":"continue"}}
{"type":"assistant","uuid":"E","parentUuid":"D","message":{"role":"assistant","content":[{"type":"text","text":"final answer"}]}}
{"type":"user", "uuid":"F","parentUuid":"C","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"t1","content":"error"}]}}
Resuming this session shows the branch A→B→C→F (the dead tool-call branch) instead of the real conversation A→B→D→E, because F is the last user/assistant record in file order.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.245 (Claude Code)
Platform
Other
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
Current model: Fable 5 (effort: high).
Bug discovered while using Claude Code VSCode extension. The extension version is also the latest, 2.1.245.
Related: #33651, #43764, #43044.