gastownhall/beads · error
target directory identity changed
Error message
target directory identity changed
What it means
bd throws this error when the target worktree's directory no longer refers to the same filesystem inode/pinned metadata as when the plan was built (checked via os.SameFile plus pinned-file metadata). The path was deleted and recreated, replaced by a symlink or bind mount, or otherwise swapped, so bd treats the target as a different directory and aborts rather than operating on an unexpected location.
Source
Thrown at cmd/bd/worktree_cmd.go:1449
}
facts.StatusBytes = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target cleanliness changed")}
}
facts.Cleanliness = worktreeremove.InvariantStable
facts.StatusBytes = worktreeremove.InvariantStable
if currentTarget.statusFingerprint != plan.target.statusFingerprint {
facts.DirtyFileFingerprint = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target changed files changed")}
}
facts.DirtyFileFingerprint = worktreeremove.InvariantStable
if !plan.force && currentTarget.status != "" {
facts.Cleanliness = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target is no longer clean")}
}
if !os.SameFile(currentTarget.pathInfo, plan.target.pathInfo) ||
!samePinnedFileMetadata(currentTarget.pathInfo, plan.target.pathInfo) {
facts.TargetDirectory = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target directory identity changed")}
}
facts.TargetDirectory = worktreeremove.InvariantStable
if !os.SameFile(currentTarget.gitDirInfo, plan.target.gitDirInfo) ||
!samePinnedFileMetadata(currentTarget.gitDirInfo, plan.target.gitDirInfo) {
facts.GitAdminDirectory = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target git directory identity changed")}
}
facts.GitAdminDirectory = worktreeremove.InvariantStable
if !os.SameFile(currentTarget.gitMarkerInfo, plan.target.gitMarkerInfo) ||
!samePinnedFileMetadata(currentTarget.gitMarkerInfo, plan.target.gitMarkerInfo) {
facts.GitMarker = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target git marker identity changed")}
}
facts.GitMarker = worktreeremove.InvariantStable
if currentTarget.gitDirFingerprint != plan.target.gitDirFingerprint {
facts.GitAdminDirectoryBytes = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target git directory identity changed (contents mismatch)")}
}View on GitHub (pinned to 71377f2769)
Solutions
- Re-run the bd command so a fresh plan captures the new directory identity (new inode/pathInfo)
- Verify the path with `git worktree list` and `ls -ld <path>`; if it was recreated unintentionally, restore the original worktree via `git worktree add` at the same commit
- If the path is now a symlink or different mount, point bd at the canonical real directory instead
- Serialize any external scripts that remove/recreate the worktree with bd operations
Example fix
// before: worktree deleted and recreated between plan and execution
plan := snapshot(wt) // inode 1001
run("git", "worktree", "remove", wt); run("git", "worktree", "add", wt, ref) // inode 2002
execute(plan) // error: target directory identity changed
// after: re-plan against the recreated directory
plan := snapshot(wt) // fresh pathInfo
execute(plan) // succeeds Defensive patterns
Strategy: validation
Validate before calling
info1, _ := os.Stat(plannedPath)
info2, err := os.Stat(wt)
if err != nil || !os.SameFile(info1, info2) {
// path was replaced/recreated; re-plan before calling bd
} Type guard
func sameDirectory(plannedPath string, wt string) bool {
a, err1 := os.Stat(plannedPath)
b, err2 := os.Stat(wt)
return err1 == nil && err2 == nil && os.SameFile(a, b)
} Try / catch
err := bdRemoveWorktree(plan)
if err != nil && strings.Contains(err.Error(), "target directory identity changed") {
plan = rebuildPlan(plan.Target) // re-stat the new directory
err = bdRemoveWorktree(plan)
} Prevention
- Don't remove/recreate the worktree while a bd plan is pending
- Resolve symlinks to the canonical path before planning (filepath.EvalSymlinks)
- Avoid moving worktrees across filesystems or remounting volumes mid-operation
- Run `git worktree list` to confirm the registration matches the on-disk directory before executing
When it happens
Trigger: Executing a planned worktree operation after the target path was removed and re-created (`git worktree remove` + re-add), replaced by a symlink, moved and restored, or its gitdir admin file metadata changed — so os.SameFile(currentPathInfo, planPathInfo) is false.
Common situations: A script or CI deleted and recreated the worktree between plan and execution; the directory was restored from a backup or moved across filesystems; a container/volume remount replaced the path; another tool re-initialized the worktree while a bd operation was pending.
Related errors
- target HEAD changed from %s to %s
- target git directory is not a real directory: %s
- failed to pin target git marker identity: %w
- cannot safely prepare .gitignore cleanup: %w
- target is no longer registered at %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2e1858e7ed9c71a8.
Report an issue: GitHub.