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

  1. Re-run the bd command so a fresh plan captures the new directory identity (new inode/pathInfo)
  2. 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
  3. If the path is now a symlink or different mount, point bd at the canonical real directory instead
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/2e1858e7ed9c71a8. Report an issue: GitHub.