{"record":{"id":"1d2f52b9cc6b3014","repo":"gastownhall/beads","slug":"root-identity-mismatch-record-has-q-workspace-h","errorCode":null,"errorMessage":"root identity mismatch (record has %q, workspace has %q)","messagePattern":"root identity mismatch \\(record has %q, workspace has %q\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":814,"sourceCode":"\t\t\t\"backend cleanup\",\n\t\t\trecordPath,\n\t\t\tpf.Pid,\n\t\t\terr,\n\t\t\tunverifiableProcessChecks{\n\t\t\t\tLiveEstablished: live,\n\t\t\t},\n\t\t)\n\t}\n\texpectedRootID, err := identity.RootID(rootDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"resolve backend root identity: %w\", err)\n\t}\n\tif pf.RootID != expectedRootID {\n\t\treturn unverifiableProcessError(\n\t\t\t\"backend cleanup\",\n\t\t\trecordPath,\n\t\t\tpf.Pid,\n\t\t\tfmt.Errorf(\"root identity mismatch (record has %q, workspace has %q)\", pf.RootID, expectedRootID),\n\t\t\tunverifiableProcessChecks{},\n\t\t)\n\t}\n\n\thandle, dead, err := openRecordedProcess(pf)\n\tif err != nil {\n\t\treturn unverifiableProcessError(\n\t\t\t\"backend cleanup\",\n\t\t\trecordPath,\n\t\t\tpf.Pid,\n\t\t\terr,\n\t\t\tunverifiableProcessChecks{},\n\t\t)\n\t}\n\tif dead {\n\t\tif _, err := quarantineRecord(rootDir, server.PIDFileName, time.Now()); err != nil {\n\t\t\treturn fmt.Errorf(\"quarantine dead backend record: %w\", err)\n\t\t}","sourceCodeStart":796,"sourceCodeEnd":832,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L796-L832","documentation":"The backend PID record's RootID does not match the current workspace's root identity. This means the record belongs to a different workspace (or the workspace was re-created/moved), so the recorded process cannot be trusted to be this workspace's backend. Cleanup refuses with an unverifiableProcessError rather than killing a possibly foreign process.","triggerScenarios":"pf.RootID != identity.RootID(rootDir) at internal/storage/dbproxy/proxy/endpoint.go:809-816 — the pidfile at <rootDir>/<PIDFileName> was written for a different workspace root than the one being cleaned.","commonSituations":"Workspace directory was copied, moved, or cloned (carrying a foreign pidfile); two workspaces sharing storage via symlink; workspace deleted and re-created while an old pidfile remained; restoring the workspace from a backup.","solutions":["Confirm no live dolt-backend process belongs to this directory (ps / tasklist for the recorded pid).","Manually quarantine the stale record: mv <recordPath> <recordPath>.stale-<unix-timestamp>, then retry cleanup.","Avoid copying or renaming workspaces while a backend is running; stop the backend first.","If workspaces share a directory via symlink/bind-mount, separate them so each has its own pidfile."],"exampleFix":"// before\nError: backend cleanup refused for unverifiable process pid 4242 ...: root identity mismatch (record has \"abc123\", workspace has \"def456\")\n// after\n$ ps -p 4242            # confirm not yours / already dead\n$ mv /ws/.beads/dbproxy/dolt-backend.pid /ws/.beads/dbproxy/dolt-backend.pid.stale-$(date +%s)\n$ bd doctor","handlingStrategy":"validation","validationCode":"// Before cleanup, verify the record belongs to this workspace:\nexpected, _ := identity.RootID(rootDir)\nvar rec struct{ RootID string }\nif data, err := os.ReadFile(recordPath); err == nil && json.Unmarshal(data, &rec) == nil && rec.RootID != expected {\n    os.Rename(recordPath, recordPath+\".stale-\"+fmt.Sprint(time.Now().Unix())) // quarantine foreign record\n}","typeGuard":"func recordBelongsToWorkspace(recRootID, wsRootID string) bool { return recRootID == wsRootID }","tryCatchPattern":"var ule *unverifiableLifecycleError\nif err := cleanupOrphanBackend(rootDir); errors.As(err, &ule) && strings.Contains(ule.Error(), \"root identity mismatch\") {\n    // foreign record: confirm recorded pid is not ours, then mv record to .stale-<ts>\n    return nil\n}","preventionTips":["Stop backends before copying, moving, or cloning a workspace.","Do not share one workspace directory between projects via symlinks.","After restoring backups, delete stale pidfiles before starting bd.","Tag workspace roots clearly and never reuse a directory for a second workspace."],"tags":["go","workspace-identity","root-mismatch","pidfile"],"backgroundTag":"root-identity-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}