{"record":{"id":"3a9ca172eb1e22d2","repo":"gastownhall/beads","slug":"target-git-marker-identity-changed","errorCode":null,"errorMessage":"target git marker identity changed","messagePattern":"target git marker identity changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/worktree_cmd.go","lineNumber":1461,"sourceCode":"\t\tfacts.Cleanliness = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"target is no longer clean\")}\n\t}\n\tif !os.SameFile(currentTarget.pathInfo, plan.target.pathInfo) ||\n\t\t!samePinnedFileMetadata(currentTarget.pathInfo, plan.target.pathInfo) {\n\t\tfacts.TargetDirectory = worktreeremove.InvariantChanged\n\t\treturn worktreeRevalidationObservation{facts: facts, err: fmt.Errorf(\"target directory identity changed\")}\n\t}\n\tfacts.TargetDirectory = worktreeremove.InvariantStable\n\tif !os.SameFile(currentTarget.gitDirInfo, plan.target.gitDirInfo) ||\n\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}","sourceCodeStart":1443,"sourceCodeEnd":1479,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/worktree_cmd.go#L1443-L1479","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: plan built, then `git worktree repair` runs elsewhere\nplan := buildRemovalPlan(target)\ngitWorktreeRepair() // rewrites .git marker\nexecuteRemoval(plan) // fails: target git marker identity changed\n// after: complete repair first, then plan+remove atomically\ngitWorktreeRepair()\nplan := buildRemovalPlan(target)\nexecuteRemoval(plan)","handlingStrategy":"retry","validationCode":"markerPath := filepath.Join(worktreePath, \".git\")\ncur, err := os.Stat(markerPath)\nif err != nil { return err }\nif !os.SameFile(cur, pinned.gitMarkerInfo) || cur.Size() != pinned.gitMarkerInfo.Size() {\n    return fmt.Errorf(\".git marker replaced; rebuild plan\")\n}","typeGuard":"func markerUnchanged(current, pinned os.FileInfo) bool {\n    return os.SameFile(current, pinned) &&\n        current.Mode() == pinned.Mode() &&\n        current.Size() == pinned.Size()\n}","tryCatchPattern":"err := bdWorktreeRemove(path)\nif err != nil && strings.Contains(err.Error(), \"git marker identity changed\") {\n    // marker file was rewritten: re-verify and retry with a fresh plan\n    err = rebuildPlanAndRemove(path)\n}","preventionTips":["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"],"tags":["git","worktree","filesystem","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"}