gastownhall/beads · error
target changed files changed
Error message
target changed files changed
What it means
bd throws this error during worktree revalidation when the fingerprint of the target's changed files (statusFingerprint) differs from the planned snapshot, even though overall cleanliness may look the same. It means the specific set of dirty files changed since planning, so the plan's assumptions about the worktree contents are stale and the operation aborts.
Source
Thrown at cmd/bd/worktree_cmd.go:1439
if currentTarget.headOID != plan.target.headOID {
facts.Head = worktreeremove.InvariantChanged
return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("target HEAD changed from %s to %s", plan.target.headOID, currentTarget.headOID)}
}
facts.Head = worktreeremove.InvariantStable
if currentTarget.status != plan.target.status {
if (currentTarget.status == "") != (plan.target.status == "") {
facts.Cleanliness = worktreeremove.InvariantChanged
} else {
facts.Cleanliness = worktreeremove.InvariantStable
}
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.InvariantStableView on GitHub (pinned to 71377f2769)
Solutions
- Re-run the bd command to rebuild the plan from the current status fingerprint
- Commit, stash, or clean the changed files so the worktree matches the planned fingerprint
- Use the force flag if proceeding with the changed file set is safe
- Find and disable the concurrent writer (formatter, build, IDE autosave) or sequence operations to avoid overlap
Example fix
// before: fingerprint drifts while operation is queued plan := snapshot(target) // fingerprint F1 autofixer.Run(wt) // fingerprint becomes F2 execute(plan) // error: target changed files changed // after: execute immediately after snapshot, or re-snapshot plan := snapshot(target) execute(plan) // no intervening writers
Defensive patterns
Strategy: validation
Validate before calling
out, _ := exec.Command("git", "-C", wt, "status", "--porcelain").Output()
if fingerprint(string(out)) != plan.Target.StatusFingerprint {
// dirty-file set changed; rebuild the plan before invoking bd
} Type guard
func dirtySetUnchanged(plannedFingerprint string, wt string) bool {
out, err := exec.Command("git", "-C", wt, "status", "--porcelain").Output()
return err == nil && fingerprint(string(out)) == plannedFingerprint
} Try / catch
err := bdRemoveWorktree(plan)
if err != nil && strings.Contains(err.Error(), "target changed files changed") {
plan = rebuildPlan(plan.Target)
err = bdRemoveWorktree(plan)
} Prevention
- Don't edit, generate, or format files in the worktree between planning and execution
- Add generated artifacts to .gitignore or direct build output outside the worktree
- Execute the operation right after snapshotting to shrink the race window
- Compare `git status --porcelain` output before invoking to detect drift early
When it happens
Trigger: Executing a planned worktree operation after the set of modified/untracked files changed — e.g. a new file was edited, one dirty file was reverted while another appeared, or untracked files were added/removed — such that `git status --porcelain` content (fingerprinted) differs from plan time.
Common situations: A build/codegen step regenerated files between plan and execution; the user edited different files while a bd operation was pending; a linter/formatter or IDE auto-fixed files; concurrent agent sessions touched different files in the same worktree.
Related errors
- target is no longer registered at %s
- target git directory changed
- target HEAD changed from %s to %s
- target cleanliness changed
- target directory identity changed
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7463f2ee1bc42b27.
Report an issue: GitHub.