EnterWorktree sometimes branches from stale local main instead of origin/main, and sometimes reuses a .claude/worktrees/<slug> dir in-place for an unrelated branch

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 29, 2026

Summary

Two related failure modes observed in EnterWorktree, both reproducible via git reflog inspection after the fact (this is a postmortem from live evidence gathered during a single multi-hour session on a private repo, not an isolated minimal repro):

  1. A new session's branch is sometimes created from the local main ref (branch: Created from main / branch: Created from <local SHA> in the reflog) instead of a freshly fetched origin/main. If local main has diverged from origin/main (e.g. unpushed local commits left over from a prior session), the new branch silently inherits that divergence.
  2. EnterWorktree sometimes reuses an existing .claude/worktrees/<slug> directory in place (git checkout <other-branch> inside it) for an unrelated session, rather than creating a new worktree directory. The directory's basename then permanently no longer matches the branch checked out inside it, and the original branch that used to live there becomes an orphaned local ref with no worktree pointing at it.

Evidence

Failure mode 1 — two branches created from local main on the same day local main had 2 unpushed, diverged commits sitting on top of a merge-base that origin/main had already moved well past:

$ git reflog show claude/vibrant-poitras-4a103f
576bdfd claude/vibrant-poitras-4a103f@{2026-07-27 03:31:26 +0900}: branch: Created from main

$ git reflog show claude/zen-lamarr-d31cdc
576bdfd claude/zen-lamarr-d31cdc@{2026-07-27 04:23:29 +0900}: branch: Created from main

$ git merge-base --is-ancestor main origin/main; echo $?
1   # NO — local main is not an ancestor of origin/main
$ git merge-base --is-ancestor origin/main main; echo $?
1   # NO — origin/main is not an ancestor of local main either
    # => true divergence, not simple staleness

Two more branches the same day, created directly from that same contaminated SHA (576bdfd220bfa5c3bb14b2f7e33ecf648f12d621) rather than by name, landing in worktree directories that don't match either the branch just created or (per failure mode 2 below) the branch now sitting there:

claude/dazzling-knuth-d5618a@{2026-07-27 03:25:32 +0900}: branch: Created from 576bdfd220bfa5c3bb14b2f7e33ecf648f12d621
claude/elated-dewdney-da6b7d@{2026-07-27 03:25:38 +0900}: branch: Created from 576bdfd220bfa5c3bb14b2f7e33ecf648f12d621

Failure mode 2 — worktree directory reused in place for an unrelated branch, caught live while a session was running inside it:

$ pwd
.../.claude/worktrees/vibrant-poitras-4a103f
$ git branch --show-current
claude/fix-escapecsv-export
$ git reflog show HEAD --date=iso
255dd6c HEAD@{2026-07-27 04:44:31 +0900}: checkout: moving from claude/vibrant-poitras-4a103f to claude/fix-escapecsv-export

The directory is named for claude/vibrant-poitras-4a103f, but an in-place git checkout swapped it to claude/fix-escapecsv-export mid-lifecycle. The original branch claude/vibrant-poitras-4a103f still existed locally afterward (tip 576bdfd) but no worktree pointed at it anymore — it was silently orphaned until cleaned up manually.

The same session that made this observation was itself, at the time, a live instance of the bug: a Claude Code session running inside .claude/worktrees/vibrant-poitras-4a103f whose branch had already been silently swapped underneath it before the session got far enough to notice the mismatch.

Suggested detection signal (what we built defensively in the meantime)

Directory-basename vs branch-name mismatch under .claude/worktrees/<slug>/ is a cheap, reliable signal for failure mode 2. For failure mode 1: check whether git merge-base --is-ancestor origin/<default-branch> <local-default-branch> is false (true divergence, not just "behind") — that distinguishes normal staleness from actual local contamination. We're running both checks as a SessionStart hook now, but it can only warn after the fact — it can't prevent the branch from being created wrong in the first place.

Impact

A session can silently build on top of unpushed, possibly-abandoned local commits instead of a clean base, and/or lose track of which branch it's actually supposed to be working on when a worktree directory is reused. Both are hard to notice without deliberately auditing git reflog. In our case it caused several hours of downstream confusion and required a manual audit + cleanup pass across ~28 concurrent worktrees to fully untangle.

Environment

  • Claude Code CLI, git worktrees under .claude/worktrees/
  • macOS (Darwin), git with extensions.worktreeConfig = true
  • Occurred across multiple sessions on 2026-07-27; repo had ~28 concurrent worktrees at time of observation, so this is not a single-worktree edge case

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗