Windows: worktree creation refused when the session's cwd spelling is not the canonical one

Status Open
Reported on v2.1.275
Maintainer reply None cached
Activity 0 comments · opened Sep 21, 2026

Summary

On Windows, creating a worktree fails whenever the process was started in a
working directory whose spelling Node's fs.realpathSync does not canonicalise —
a lowercase drive letter, a lowercase path segment, or an 8.3 short name. The
worktree directory is created, but entering it is refused.

The refusal is emitted by the guard that compares git's reported working tree
against the path Claude Code holds. In every failing case the two strings denote
the same directory and differ only in spelling.

The message blames a core.worktree redirect. No such setting exists in the
repository, so it sends people looking in the wrong place.

Reproduced on 2.1.275 and 2.1.278.

The rule that predicts every result

A spelling is refused exactly when fs.realpathSync(cwd) !== fs.realpathSync.native(cwd).

| cwd passed to the process | process.cwd() | realpathSync | realpathSync.native | worktree |
|---|---|---|---|---|
| C:\Users\dev\myrepo | as given | C:\Users\dev\myrepo | C:\Users\dev\myrepo | created |
| C:/Users/dev/myrepo | normalised to \ | C:\Users\dev\myrepo | C:\Users\dev\myrepo | created |
| C:\Users\dev\myrepo\ | trailing sep dropped | C:\Users\dev\myrepo | C:\Users\dev\myrepo | created |
| c:\Users\dev\myrepo | as given | c:\Users\dev\myrepo | C:\Users\dev\myrepo | refused |
| C:\users\dev\myrepo | as given | C:\users\dev\myrepo | C:\Users\dev\myrepo | refused |
| C:\Users\dev\MYREPO~1 | as given | C:\Users\dev\MYREPO~1 | C:\Users\dev\myrepo | refused |

6 of 6. The three refused spellings are the three where the two realpath variants
disagree.

Error text

Refusing to use c:\Users\dev\myrepo\.claude\worktrees\case-probe-lower as an
isolation worktree: git resolves its working tree to
C:/Users/dev/myrepo/.claude/worktrees/case-probe-lower (a core.worktree
redirect, or a checkout discovered above it), so commands run there would write
outside the worktree. Remove the redirect, restore the worktree's own .git, or
recreate the worktree, then retry.

The message prints both spellings of the same path side by side.

git is not the source of the mismatch

From every one of the six spellings above, run in that directory:

git rev-parse --show-toplevel      -> C:/Users/dev/myrepo
git rev-parse --absolute-git-dir   -> C:/Users/dev/myrepo/.git
git config --get core.worktree     -> unset (exit 1)

git canonicalises regardless of how the directory was entered, and there is no
core.worktree anywhere. The differing string is the one the process carries in
process.cwd().

Likely cause (inferred from the shipped binary, not from source)

In 2.1.278 the refusal comes from a comparison chain whose lowest level is a
string comparison taking a case-insensitivity flag:

function RNe(e, n, r) {
  let s = r ? e.toLowerCase() : e;
  let g = r ? n.toLowerCase() : n;
  if (s === g) return true;
  ...
}

The caller on this path passes false. A sibling comparator in the same binary
passes true and carries a dedicated hint for case-only differences, which this
path does not use:

` (this path differs from the registered spelling only by letter case — respell it to match ${r} exactly)`

The canonicaliser on this path appears to follow symlinks only and never to ask
Windows for the final path name, which would explain why 8.3 short names — where
case is already correct — are refused as well.

This attribution comes from reading strings in the packaged binary, so treat it
as a strong hypothesis rather than a confirmed call path. The behavioural table
above does not depend on it.

Two consequences worth fixing alongside

  1. Every refusal leaves state behind. The worktree is created, registered and

locked, and its branch is created, before the guard rejects it. Recovering
takes git worktree unlock, git worktree remove --force, git worktree prune
and git branch -D. Both the tool call and the -w <name> CLI flag behave
this way.

  1. The message names a cause that is not present. Pointing at core.worktree

costs the user a round of investigating git config that cannot find anything.

Suggested fix

Canonicalise both sides with the native realpath (or compare case-insensitively
on Windows) before this guard runs. Failing that, attach the existing case-only
hint to this error so the message names the real cause.

Workaround

Start Claude Code from the canonical spelling of the path — the one
fs.realpathSync.native returns.

What was ruled out

  • core.worktree — unset in the main repo and in both worktrees.
  • extensions.worktreeConfig — absent.
  • Malformed .git / gitdir files in the created worktree — contents correct.
  • Test ordering — reversing the order gives identical results.
  • Forward slashes and a trailing separator — both succeed; process.cwd()

normalises them.

  • GIT_WORK_TREE, GIT_DIR, GIT_COMMON_DIR, GIT_CEILING_DIRECTORIES — all unset.
  • NTFS per-directory case sensitivity — disabled on the repository and its parent.
  • "Only the tool call is affected" — the -w CLI flag fails the same way, exit code 1.

Not tested

UNC paths, subst virtual drives, and NTFS directory junctions. No syscall trace
was taken, so the call path above is inferred rather than observed.

Affected versions

| Version | Behaviour with a non-canonical cwd spelling |
|---|---|
| 2.1.218 | created (the guard's message is absent from this build) |
| 2.1.275 | refused |
| 2.1.278 | refused |

A control search in the 2.1.218 binary finds EnterWorktree 19 times,
worktree 448 times and ExitWorktree 9 times, so the message's absence there is
a real absence rather than a failed search. The pre-2.1.226 guard normalised both
sides with toLowerCase() before comparing, which suggests this arrived as a
regression when the check was rewritten.

Environment

  • Windows 10 Pro 10.0.19045, Node v25.8.1, Git for Windows
  • Reproduced with the 2.1.275 build shipped inside the Claude desktop app and

with the published @anthropic-ai/claude-code-win32-x64@2.1.278 binary run from
a temporary directory

  • A second developer on Windows reported a failure with the same error text.

Their exact version and paths have not been collected, so whether their case has
the same trigger is not confirmed.

View original on GitHub ↗