gastownhall/beads · error
target git marker identity changed
Error message
target git marker identity changed
What it means
This error comes from the same pre-execution revalidation in `bd worktree remove`. bd pins the FileInfo of the worktree's git marker (the `.git` file or the marker distinguishing the linked worktree) at plan time and, at execution time, checks `os.SameFile` plus mode/size/mtime. If the marker file was deleted and recreated (new inode) or its metadata changed, bd refuses to proceed: the removal plan was authorized against a different filesystem object than the one now present.
Source
Thrown at cmd/bd/worktree_cmd.go:1461
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)")}
}
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)}
}
}View on GitHub (pinned to 71377f2769)
Solutions
- Re-run `bd worktree remove` to build a fresh plan against the current marker state
- Check whether another process (git command, CI, sync daemon) rewrote the `.git` marker; stop concurrent modification first
- Inspect the marker content (`cat <worktree>/.git`) to confirm it still points at the expected admin directory before retrying
- If the marker was legitimately changed, treat it as a new worktree and remove it with a new command instead of forcing the old plan
Example fix
// before: plan built, then `git worktree repair` runs elsewhere plan := buildRemovalPlan(target) gitWorktreeRepair() // rewrites .git marker executeRemoval(plan) // fails: target git marker identity changed // after: complete repair first, then plan+remove atomically gitWorktreeRepair() plan := buildRemovalPlan(target) executeRemoval(plan)
Defensive patterns
Strategy: retry
Validate before calling
markerPath := filepath.Join(worktreePath, ".git")
cur, err := os.Stat(markerPath)
if err != nil { return err }
if !os.SameFile(cur, pinned.gitMarkerInfo) || cur.Size() != pinned.gitMarkerInfo.Size() {
return fmt.Errorf(".git marker replaced; rebuild plan")
} Type guard
func markerUnchanged(current, pinned os.FileInfo) bool {
return os.SameFile(current, pinned) &&
current.Mode() == pinned.Mode() &&
current.Size() == pinned.Size()
} Try / catch
err := bdWorktreeRemove(path)
if err != nil && strings.Contains(err.Error(), "git marker identity changed") {
// marker file was rewritten: re-verify and retry with a fresh plan
err = rebuildPlanAndRemove(path)
} Prevention
- Don't run `git worktree repair`, `git worktree move`, or re-init while a removal plan is pending
- Treat the worktree's `.git` file as immutable between planning and execution
- Check for background agents or IDE git integrations before removing a worktree
- If a retry loop recreates worktrees, remove and recreate in one serialized step
When it happens
Trigger: Calling `bd worktree remove` after the worktree's `.git` marker file was replaced or rewritten between plan and execution — e.g. `git worktree repair`, re-cloning into the same path, a script rewriting the `.git` pointer file, or metadata-touching tools (touch, chmod, backup restore) altering mode/size/mtime.
Common situations: Concurrent `git worktree repair`/`add` from another terminal or agent; container or mount remounting replaced the file; an editor or linter rewrote the `.git` file; running the remove command in a retry loop against a worktree that another process keeps refreshing.
Related errors
- target git directory identity changed
- target HEAD disagrees with git worktree registry (registry %
- target git directory is not a real directory: %s
- failed to pin target git marker identity: %w
- cannot safely prepare .gitignore cleanup: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3a9ca172eb1e22d2.
Report an issue: GitHub.