shareAI-lab/learn-claude-code · error · ValueError
Worktree '{name}' already exists in index
Error message
Worktree '{name}' already exists in index What it means
Thrown by WorktreeManager.create when _find(name) locates an entry with the same name in the persisted worktree index JSON. Creation is idempotent-hostile by design: each worktree name maps to one directory (self.dir / name) and one branch (wt/{name}), so a duplicate would overwrite state. The check reads the index file, so it also fires when a previous worktree was created but never removed from the index.
Source
Thrown at agents/s12_worktree_task_isolation.py:287
self.index_path.write_text(json.dumps(data, indent=2))
def _find(self, name: str) -> dict | None:
idx = self._load_index()
for wt in idx.get("worktrees", []):
if wt.get("name") == name:
return wt
return None
def _validate_name(self, name: str):
if not re.fullmatch(r"[A-Za-z0-9._-]{1,40}", name or ""):
raise ValueError(
"Invalid worktree name. Use 1-40 chars: letters, numbers, ., _, -"
)
def create(self, name: str, task_id: int = None, base_ref: str = "HEAD") -> str:
self._validate_name(name)
if self._find(name):
raise ValueError(f"Worktree '{name}' already exists in index")
if task_id is not None and not self.tasks.exists(task_id):
raise ValueError(f"Task {task_id} not found")
path = self.dir / name
branch = f"wt/{name}"
self.events.emit(
"worktree.create.before",
task={"id": task_id} if task_id is not None else {},
worktree={"name": name, "base_ref": base_ref},
)
try:
self._run_git(["worktree", "add", "-b", branch, str(path), base_ref])
entry = {
"name": name,
"path": str(path),
"branch": branch,
"task_id": task_id,View on GitHub (pinned to 985456f4ad)
Solutions
- If the existing worktree is still wanted, reuse its entry from the index instead of creating a new one
- If it is stale, call the manager's remove/delete flow for that name first, then create again
- Make names unique per task, e.g. f"task-{task_id}-{slug}", so parallel tasks cannot collide
- Catch ValueError and inspect the index entry to decide reuse-vs-replace programmatically
Example fix
// before
wt.create(name="feature-x", task_id=7) # raises on retry
// after
existing = wt._find("feature-x")
if existing:
result = existing["path"] # reuse
else:
result = wt.create(name="feature-x", task_id=7) Defensive patterns
Strategy: validation
Validate before calling
entry = wt._find(name)
if entry:
use_path = entry["path"] # reuse instead of create
else:
use_path = wt.create(name, task_id=tid) Try / catch
try:
path = wt.create(name, task_id=tid)
except ValueError as e:
if 'already exists' in str(e):
existing = wt._find(name)
if existing and stale(existing):
wt.remove(name)
path = wt.create(name, task_id=tid)
else:
path = existing["path"]
else:
raise Prevention
- Make worktree names unique per task (include task_id in the name)
- Always pair create with a matching remove in cleanup/finally blocks
- Treat create as get-or-create: check the index first
When it happens
Trigger: Calling create("feature-x") twice without a matching remove/delete in between; a prior create that succeeded on disk but whose caller crashed before the flow completed, leaving the index entry behind; two agents racing to create the same-named worktree.
Common situations: Retrying a failed orchestration step that re-runs the same create call; stale index entries after interrupted cleanup; deriving the same slug from two different task subjects that sanitize to an identical name.
Related errors
- Not in a git repository. worktree tools require git.
- git {' '.join(args)} failed
- Invalid worktree name. Use 1-40 chars: letters, numbers, .,
- Worktree path escapes directory: {name!r}
- Task {task_id} not found
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/b43f8dc61d16ca80.
Report an issue: GitHub.