gastownhall/beads · error
target HEAD disagrees with git worktree registry (registry %
Error message
target HEAD disagrees with git worktree registry (registry %q, target %q)
What it means
inspectWorktreeTarget compares the HEAD commit observed in the target worktree with the headOID recorded in bd's worktree registry. If they differ (or HEAD is empty), removal is refused. This prevents removing a worktree whose state changed since registration, guarding against racing mutations.
Source
Thrown at cmd/bd/worktree_cmd.go:998
return pinnedWorktreeTarget{}, fmt.Errorf("target HEAD does not resolve to a commit: %w", err)
}
statusOutput, err := git.output(
ctx,
worktree.path,
"status",
"--porcelain=v1",
"-z",
"--untracked-files=all",
"--ignore-submodules=none",
"--ignored=matching",
)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to inspect target cleanliness: %w", err)
}
headOID := strings.TrimSpace(string(headOutput))
if headOID == "" || headOID != worktree.headOID {
return pinnedWorktreeTarget{}, fmt.Errorf(
"target HEAD disagrees with git worktree registry (registry %q, target %q)",
worktree.headOID,
headOID,
)
}
pathInfo, err := os.Lstat(worktree.path)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to pin target directory identity: %w", err)
}
if pathInfo.Mode()&os.ModeSymlink != 0 || !pathInfo.IsDir() {
return pinnedWorktreeTarget{}, fmt.Errorf("target path is not a real directory: %s", worktree.path)
}
gitDirInfo, err := os.Lstat(gitDir)
if err != nil {
return pinnedWorktreeTarget{}, fmt.Errorf("failed to pin target git directory identity: %w", err)
}
if gitDirInfo.Mode()&os.ModeSymlink != 0 || !gitDirInfo.IsDir() {
return pinnedWorktreeTarget{}, fmt.Errorf("target git directory is not a real directory: %s", gitDir)View on GitHub (pinned to 71377f2769)
Solutions
- Check current HEAD with `git -C <path> rev-parse HEAD` and refresh/re-register the worktree entry so the registry matches, then retry.
- If the movement was intentional, re-run `bd worktree remove` so a fresh inspection pins the new HEAD.
- Quiesce any process mutating the worktree (stop CI/agents) before removing.
- If the registry entry is stale or corrupt, remove it and re-register from `git worktree list`.
Example fix
// before bd worktree remove feature-x // error: target HEAD disagrees with git worktree registry (registry "abc123", target "def456") // after git -C /repo/.worktrees/feature-x rev-parse HEAD # confirm intended state bd worktree remove feature-x # re-run with refreshed registry
Defensive patterns
Strategy: retry
Validate before calling
const head = Bun.spawnSync(["git", "-C", path, "rev-parse", "HEAD"]).stdout.toString().trim();
const reg = JSON.parse(Bun.spawnSync(["bd", "worktree", "list", "--json"]).stdout.toString());
const entry = reg.find(w => w.path === path);
if (entry && entry.headOid && entry.headOid !== head) throw new Error("registry drift — refresh before removing"); Try / catch
try {
await bd("worktree", "remove", path);
} catch (e) {
if (String(e.message).includes("target HEAD disagrees")) {
await bd("worktree", "remove", path); // re-run: fresh inspection pins new HEAD
} else throw e;
} Prevention
- Don't mutate the worktree (commit/switch/reset) while a removal is in flight or a prompt is open.
- Stop CI jobs/agents operating on the worktree before removing.
- Refresh the registry after intentional HEAD changes.
- Avoid copying registry state between machines.
When it happens
Trigger: The worktree's HEAD moved (new commits, branch switch, reset) between registration and the removal call; a stale registry entry; observeRevalidation detects drift during a post-confirmation re-check.
Common situations: Someone committed or switched branches while the removal prompt was open; a CI job or agent pushed/reset the branch concurrently; registry data copied from another machine with a different HEAD; registry out of sync after a manual `git worktree add`.
Related errors
- target is no longer registered at %s
- registered target identity changed
- target git directory changed
- target common git directory changed
- target HEAD changed from %s to %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4972a507440f7bf2.
Report an issue: GitHub.