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.InvariantStable

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the bd command to rebuild the plan from the current status fingerprint
  2. Commit, stash, or clean the changed files so the worktree matches the planned fingerprint
  3. Use the force flag if proceeding with the changed file set is safe
  4. 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

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


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