gastownhall/beads · error

target git directory identity changed (contents mismatch)

Error message

target git directory identity changed (contents mismatch)

What it means

Raised during the revalidation phase of `bd worktree remove`: although the worktree's git admin directory is still the same inode (identity check passed), its CONTENTS no longer match the fingerprint captured at plan time. This means something wrote into the admin directory (e.g. HEAD moved, new refs, config edits, index updates) after the removal plan was built, so bd aborts rather than removing a directory in a state different from what was inspected.

Source

Thrown at cmd/bd/worktree_cmd.go:1466

		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)")}
	}
	facts.GitAdminDirectoryBytes = worktreeremove.InvariantStable
	if currentTarget.gitMarkerFingerprint != plan.target.gitMarkerFingerprint {
		facts.GitMarkerBytes = worktreeremove.InvariantChanged
		return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf("registered target identity changed (git marker mismatch)")}
	}
	facts.GitMarkerBytes = worktreeremove.InvariantStable
	if plan.gitignoreCleanup != nil {
		if err := plan.gitignoreCleanup.validate(); err != nil {
			facts.ManagedIgnore = worktreeremove.InvariantChanged
			return worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(".gitignore changed before removal: %w", err)}
		}
	}
	facts.ManagedIgnore = worktreeremove.InvariantStable

	if plan.comparator == nil {
		facts.Comparator = worktreeremove.InvariantNotRequired
		facts.Containment = worktreeremove.InvariantNotRequired

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run `bd worktree remove` so the plan is rebuilt with the current admin-directory fingerprint
  2. Stop concurrent git activity in the worktree (auto-fetch, IDE git integration, other agents) before removing
  3. Check `git -C <worktree> log --oneline -1` and `git worktree list` to see what changed since planning; confirm the current state is safe to remove
  4. If the new contents include unmerged or valuable work, export it (commit/stash/branch) before removal

Example fix

// before: fetch/commit running in the worktree while plan is stale
plan := buildRemovalPlan(target)
gitFetchInBackground(worktree)
executeRemoval(plan) // fails: contents mismatch
// after: quiesce the worktree, then plan and remove
waitForGitQuiesce(worktree)
plan := buildRemovalPlan(target)
executeRemoval(plan)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure no git process is active in the worktree before planning removal:
out, err := exec.Command("git", "-C", worktreePath, "status", "--porcelain").Output()
if err != nil { return err } // worktree must be a stable, working repo
// Disable background maintenance that would mutate the admin dir:
exec.Command("git", "-C", worktreePath, "config", "maintenance.auto", "false").Run()

Try / catch

err := bdWorktreeRemove(path)
if err != nil && strings.Contains(err.Error(), "contents mismatch") {
    // admin dir contents changed (HEAD/refs/config): inspect then retry
    inspectWorktree(path)
    err = rebuildPlanAndRemove(path)
}

Prevention

When it happens

Trigger: Calling `bd worktree remove` where, between plan and execution, any file inside the worktree's `.git` admin directory changed content: a `git commit`/`fetch`/`rebase` updated HEAD or refs, `git config` edits, gc ran, or another agent executed git commands in that worktree.

Common situations: A background process or IDE auto-fetching in the worktree; another agent committing in the worktree while a remove was queued; cron jobs running `git gc` or `git maintenance`; running removal from a script that also performs git operations on the same worktree.

Related errors


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