Worktree-relocated background agent sessions crash-loop on reopen from the agents view ("No conversation found with session ID")

Status Open
Reported on v2.1.162
Maintainer reply None cached
Activity 4 comments · opened Jun 3, 2026

Background agent sessions relocated into a git worktree crash-loop on reopen from the agents view ("No conversation found with session ID")

Summary

Opening certain entries from the agents view crashes the worker repeatedly with:

No conversation found with session ID: <uuid>

[worker crashed (exit 1) — respawning…]

The worker respawns ~4 times, then gives up and drops back to the agents view. The broken entry stays in the list — it is not dismissed by the crash and only disappears after Ctrl-X. I've hit this at least 5 times across sessions.

Root cause is a path/cwd mismatch for background sessions that were relocated into a git worktree: the conversation transcript is stored under the worktree's encoded project directory, but the background job that the agents view lists records cwd/originCwd as the parent repository. On reopen, the worker is launched with cwd = parent repo and resumes by explicit session ID; the conversation set it loads is scoped to the parent-repo project and does not contain the worktree transcript, so the explicit-ID lookup fails (failure_reason: "not_found_explicit_id") and the worker calls process.exit(1).

This is closely related to #5768 (resume only works from the directory where the session was started) — it's a specific, crash-looping instance of that cwd-keyed-resume family — and adjacent to #57920 (worktree transcript not found on --resume), but distinct: the worktree here is live (not removed), and the failure surfaces via the agents view with a worker crash/respawn loop rather than a one-shot CLI error.

Environment

  • Claude Code 2.1.162 (macOS)
  • The affected job record was created under 2.1.161; the bug still reproduces on 2.1.162.
  • Sessions were relocated into git worktrees under <repo>/.claude/worktrees/<branch>/ (a worktree-per-task workflow).

Steps to reproduce

  1. From a repo root (e.g. /Users/USER/dev/myrepo), launch/spawn a background agent session.
  2. During that session, relocate it into a git worktree (e.g. /Users/USER/dev/myrepo/.claude/worktrees/<branch>/). The transcript is now written under the project directory encoded from the worktree path.
  3. Let the job reach a terminal state (state: "done").
  4. Later, from the agents view, select that session to reopen it.

Expected: the conversation resumes (the transcript exists and is valid).

Actual: No conversation found with session ID: <uuid>[worker crashed (exit 1) — respawning…], repeated ~4× before returning to the agents view. The entry persists in the list until removed with Ctrl-X.

Evidence / diagnosis

For one crashing session (<uuid>):

The transcript exists and is valid — only under the worktree-encoded project dir:

$ find ~/.claude/projects -name '<uuid>.jsonl'
~/.claude/projects/-Users-USER-dev-myrepo--claude-worktrees-<branch>/<uuid>.jsonl   # valid, hundreds of lines

# NOT present under the parent-repo project dir, which is where a cwd-scoped lookup searches:
$ ls ~/.claude/projects/-Users-USER-dev-myrepo/<uuid>.jsonl
No such file or directory

Note the -- in myrepo--claude-worktrees: the project-dir encoding maps both / and . to -, so /myrepo/.claude/worktrees-myrepo--claude-worktrees. The worktree itself is livegit worktree list shows the branch checked out there. This is not a deleted-worktree problem.

The job record that backs the agents-view entry — ~/.claude/jobs/<jobid>/state.json — has the mismatch:

{
  "state": "done",
  "resumeSessionId": "<uuid>",
  "linkScanPath": "/Users/USER/.claude/projects/-Users-USER-dev-myrepo--claude-worktrees-<branch>/<uuid>.jsonl",
  "cwd":       "/Users/USER/dev/myrepo",   // parent repo, NOT the worktree
  "originCwd": "/Users/USER/dev/myrepo",
  "cliVersion": "2.1.161"
}

The job already stores the correct transcript location in linkScanPath, but cwd/originCwd point at the parent repo. The resume path keys off cwd + explicit session ID, not linkScanPath.

The failing code path (from the bundle; 2.1.160/2.1.161 shown, logic unchanged in 2.1.162):

{entrypoint:"cli_flag", success:!1, failure_reason:"not_found_explicit_id"});
let rT = `No conversation found with session ID: ${id}`;
return V(rT,{level:"error"}), await exit1Fn();   // exit1Fn === process.exit(1)

exit(1) is the "worker crashed (exit 1)"; the supervisor respawns, re-attempts the same explicit ID against the same (wrong) cwd-scoped conversation set, and exits 1 again — producing the bounded respawn loop.

This is a class, not a one-off. Scanning all background jobs with a state.json, multiple carry the same signature (transcript/linkScanPath under a *worktrees* project dir while cwd/originCwd is the parent repo). I've personally had to Ctrl-X / kill at least 5 such entries across sessions.

Two observations bundled here

  1. The crash loop on reopen (primary): cwd-scoped explicit-ID resume can't find a transcript that lives under the worktree project dir.
  2. The entry isn't dismissed by the crash — it persists in the agents view because it is a state: "done" job directory at ~/.claude/jobs/<jobid>/; the crash doesn't update or remove that backing record, so only Ctrl-X clears it. Arguably the worker should not exit(1) (taking down the TUI worker) on a recoverable "conversation not found" — it should surface a non-fatal error and leave the agents view responsive.

Suggested fixes (any one breaks the loop)

  • On resume, resolve the conversation from the job's linkScanPath (or search across all project dirs by session ID) instead of only the cwd-derived project dir; or record the worktree path as the job's cwd/originCwd when the session is relocated into a worktree, so respawn launches the worker in the directory whose project dir holds the transcript.
  • Make "conversation not found" non-fatal in the worker: show an error and return to the agents view instead of process.exit(1) + respawn. This caps the blast radius even if the lookup still misses.
  • When a reopen permanently fails, mark/prune the agents-view entry so the user isn't forced to Ctrl-X every stale row.

Workaround (for affected users)

The transcript is intact; only the resume path is broken. Re-launching claude from inside the worktree directory itself (so cwd matches the transcript's project dir) and resuming by ID works. The crash itself is harmless — it can't corrupt the transcript — but the entry must be cleared from the agents view with Ctrl-X.

Related

  • #5768 — resume only works from the directory where the session was started (the underlying cwd-keyed-resume family).
  • #57920 — worktree transcript not found on --resume after the worktree path is removed (similar mechanism; that one requires the dir to be removed and has no crash loop / agents-view surface).
  • #60106 — agents-view orphans a session via clear(new)/clear(reset) (agents-view-orphan surface, no worktree, no crash loop).

View original on GitHub ↗

4 Comments

github-actions[bot] · 2 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/57920
  2. https://github.com/anthropics/claude-code/issues/5768

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

SynVisions · 2 months ago

#5768 is the closest possible duplicate, but if it is a duplicate it would be an implementation detail unknown to me. I'm not using --resume directly. It's possible that agent view is, I don't know for sure.

kcarriedo · 2 months ago

This is a path/cwd mismatch -- the job record has cwd pointing at the parent repo, but the transcript was written under the worktree project dir (~/.claude/projects/-Users-...-worktrees-<branch>/). Resume keyed off cwd + session ID fails to find it.

Short-term workaround while this gets fixed:

  1. After relocating a session into a worktree, immediately run /session save <name> to write a stable pointer before the crash window.
  2. If you hit the crash-loop, go to ~/.claude/projects/ and find the worktree-scoped directory manually. The transcript JSONL is there. You can force-resume with claude --resume <session-id> --project-dir <worktree-path> from inside the worktree directory.
  3. The Ctrl-X dismiss is the only current escape hatch from the stale agents view entry -- confirm that is working for you.

The root fix is the resume logic should use linkScanPath from the job record instead of reconstructing the path from cwd + session ID. That is clearly the intent given linkScanPath is already stored correctly.

Starosdev · 1 month ago

Same root cause, two additions from a VS Code extension repro: a different trigger/surface, and a failure mode that turns this from "cannot reopen" into "permanently stranded".

Variant: extension tab pinned to launch cwd across a restart

The trigger here is EnterWorktree rather than a background-agent relocation, and the surface is the VS Code extension rather than the agents view.

  • Claude Code extension 2.1.220, CLI 2.1.220, macOS (darwin 25.5.0)
  • Session launched with cwd = parent repo, then called EnterWorktree, moving cwd to ~/worktrees/<repo>/<branch>

The extension stores the tab against the cwd the session launched from and replays that pair on cold start. From the extension log:

[info] Received message from webview: {"type":"launch_claude","channelId":"...","cwd":"/Users/USER/repos/myrepo","resume":"<uuid>","permissionMode":"auto"}
[info] Spawning Claude with SDK query function - cwd: /Users/USER/repos/myrepo, ..., resume: <uuid>
[info] From claude: No conversation found with session ID: <uuid>
[error] Error spawning Claude (on channel ...): Error: Claude Code returned an error result: No conversation found with session ID: <uuid>
[error] Failed to load config cache: Error: Claude Code returned an error result: ...
[error] Error processing client request: Error: Claude Code returned an error result: ...

Note that the single lookup failure cascades into Failed to load config cache and Error processing client request.

While the extension host stays alive it holds the channel in memory, so the mismatch is invisible. Only a cold restart replays the stored resume + cwd pair, which is why this reads to users as "restarting VS Code loses my session".

The split-transcript symptom from #78940 also reproduces here: the custom-title record went to the launch-cwd project dir as a 119-byte stub while the body (3.5 MB) went to the worktree-encoded dir.

-Users-USER-repos-myrepo/<uuid>.jsonl                            119 B   # custom-title only
-Users-USER-worktrees-myrepo-<branch>/<uuid>.jsonl         3,510,396 B   # full transcript

The part that is not yet reported: worktree deletion makes it permanent

Every existing report in this family assumes the worktree still exists, so the transcript is at least reachable by cd-ing back into it. In a worktree-per-task workflow the worktree is routinely removed after merge, and at that point:

  • no surface lists the session, because the extension enumerates by workspace cwd and that project dir no longer corresponds to any directory a user can open,
  • --resume <id> cannot be made to work from anywhere, because the only cwd that resolves to that project dir no longer exists,
  • the transcript is still fully intact on disk, just unaddressable.

On one machine this had silently accumulated 13 orphaned worktree-scoped project dirs holding 18 transcripts (~16.5 MB), 7 of them substantive multi-MB sessions, with no user-visible indication that anything had been retained.

Recovery is possible but requires knowing the encoding scheme: a project dir name is the cwd with every non-alphanumeric character replaced by -, which is lossy and one-way, so a stranded dir name cannot be decoded back to a path. The transcript's own cwd field is the only reliable source. Copying the transcript into the parent repo's project dir restores it to the session list.

One caveat for anyone auditing their own machine for this: transcripts whose records all carry "isSidechain":true are subagent runs, not resumable conversations. They are expected to fail an explicit-ID resume and are not evidence of this bug.

Suggestion

Beyond fixing the cwd rebinding, a session whose recorded cwd no longer exists is currently invisible rather than merely broken. Falling back to the parent repo, or surfacing such sessions in the picker with their recorded cwd, would make the data recoverable without reverse-engineering the directory naming.

Happy to supply full logs if useful.