{"record":{"id":"b73be430e08dc3ab","repo":"gastownhall/beads","slug":"registered-target-identity-changed-git-marker-mis","errorCode":null,"errorMessage":"registered target identity changed (git marker mismatch)","messagePattern":"registered target identity changed \\(git marker mismatch\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/worktree_cmd.go","lineNumber":1471,"sourceCode":"\t\t!samePinnedFileMetadata(currentTarget.gitDirInfo, plan.target.gitDirInfo) {\n\t\tfacts.GitAdminDirectory = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"target git directory identity changed\")}\n\t}\n\tfacts.GitAdminDirectory = worktreeremove.InvariantStable\n\tif !os.SameFile(currentTarget.gitMarkerInfo, plan.target.gitMarkerInfo) ||\n\t\t!samePinnedFileMetadata(currentTarget.gitMarkerInfo, plan.target.gitMarkerInfo) {\n\t\tfacts.GitMarker = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"target git marker identity changed\")}\n\t}\n\tfacts.GitMarker = worktreeremove.InvariantStable\n\tif currentTarget.gitDirFingerprint != plan.target.gitDirFingerprint {\n\t\tfacts.GitAdminDirectoryBytes = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"target git directory identity changed (contents mismatch)\")}\n\t}\n\tfacts.GitAdminDirectoryBytes = worktreeremove.InvariantStable\n\tif currentTarget.gitMarkerFingerprint != plan.target.gitMarkerFingerprint {\n\t\tfacts.GitMarkerBytes = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"registered target identity changed (git marker mismatch)\")}\n\t}\n\tfacts.GitMarkerBytes = worktreeremove.InvariantStable\n\tif plan.gitignoreCleanup != nil {\n\t\tif err := plan.gitignoreCleanup.validate(); err != nil {\n\t\t\tfacts.ManagedIgnore = worktreeremove.InvariantChanged\n\t\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\".gitignore changed before removal: %w\", err)}\n\t\t}\n\t}\n\tfacts.ManagedIgnore = worktreeremove.InvariantStable\n\n\tif plan.comparator == nil {\n\t\tfacts.Comparator = worktreeremove.InvariantNotRequired\n\t\tfacts.Containment = worktreeremove.InvariantNotRequired\n\t\treturn worktreeRevalidationObservation{facts: facts}\n\t}\n\tcurrentComparator, err := plan.resolveComparator(ctx, currentTarget)\n\tif err != nil {\n\t\treturn worktreeRevalidationObservation{facts: facts, err: err}","sourceCodeStart":1453,"sourceCodeEnd":1489,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/worktree_cmd.go#L1453-L1489","documentation":"Raised during `bd worktree remove` revalidation: the fingerprint of the worktree's git marker (`.git` pointer file) content differs from the one captured when the removal plan was built. Even though the file itself is the same inode, its contents were rewritten (e.g. now pointing at a different admin directory or a different gitdir path). bd aborts because the registered worktree it plans to remove now references different git internals than were verified.","triggerScenarios":"Calling `bd worktree remove` after the `.git` marker's text was rewritten between plan and execution: `git worktree repair` or `git worktree move` re-pointed it, a manual edit of the `.git` file, re-initialization of the worktree, or a tool normalizing/rewriting gitdir paths.","commonSituations":"Running `git worktree move` on the main repo (changing relative gitdir paths inside markers); a script that regenerates worktrees in place; repository relocation or mount-path changes causing git to rewrite markers; manual edits while debugging.","solutions":["Re-run `bd worktree remove` to rebuild the plan against the marker's current contents","Find what rewrote the marker (`git worktree list`, `git worktree repair --dry-run`) and complete that operation first, then remove","Verify the marker now points at the expected admin directory (`cat <worktree>/.git`); if it points elsewhere, the old plan is unsafe — discard it","If the worktree was intentionally re-pointed, remove it with a fresh command rather than forcing the stale plan"],"exampleFix":"// before: marker re-pointed by `git worktree move` after planning\nplan := buildRemovalPlan(target)\ngitWorktreeMove(mainRepo)\nexecuteRemoval(plan) // fails: git marker mismatch\n// after: move first, then plan and remove\nrepoPath := gitWorktreeMove(mainRepo)\nplan := buildRemovalPlan(targetAt(repoPath))\nexecuteRemoval(plan)","handlingStrategy":"validation","validationCode":"// Compare the marker's current content with what was planned against:\nmarker := filepath.Join(worktreePath, \".git\")\ncur, err := os.ReadFile(marker)\nif err != nil { return err }\nif string(cur) != pinned.gitMarkerContent {\n    return fmt.Errorf(\"marker re-pointed to %q; rebuild plan\", string(cur))\n}","typeGuard":null,"tryCatchPattern":"err := bdWorktreeRemove(path)\nif err != nil && strings.Contains(err.Error(), \"git marker mismatch\") {\n    // marker contents rewritten: verify new target, then retry\n    verifyMarkerTarget(path)\n    err = rebuildPlanAndRemove(path)\n}","preventionTips":["Don't move the main repository or run `git worktree move/repair` while removal is pending","Never hand-edit the `.git` marker of a worktree being removed","Watch for tooling that regenerates or normalizes gitdir paths in markers","Plan and execute removal without intervening git commands in the same repo"],"tags":["git","worktree","fingerprint","race-condition"],"backgroundTag":"worktree-state-changed-underneath","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}