gastownhall/beads · error
root identity mismatch (record has %q, workspace has %q)
Error message
root identity mismatch (record has %q, workspace has %q)
What it means
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.
Source
Thrown at internal/storage/dbproxy/proxy/endpoint.go:814
"backend cleanup",
recordPath,
pf.Pid,
err,
unverifiableProcessChecks{
LiveEstablished: live,
},
)
}
expectedRootID, err := identity.RootID(rootDir)
if err != nil {
return fmt.Errorf("resolve backend root identity: %w", err)
}
if pf.RootID != expectedRootID {
return unverifiableProcessError(
"backend cleanup",
recordPath,
pf.Pid,
fmt.Errorf("root identity mismatch (record has %q, workspace has %q)", pf.RootID, expectedRootID),
unverifiableProcessChecks{},
)
}
handle, dead, err := openRecordedProcess(pf)
if err != nil {
return unverifiableProcessError(
"backend cleanup",
recordPath,
pf.Pid,
err,
unverifiableProcessChecks{},
)
}
if dead {
if _, err := quarantineRecord(rootDir, server.PIDFileName, time.Now()); err != nil {
return fmt.Errorf("quarantine dead backend record: %w", err)
}View on GitHub (pinned to 71377f2769)
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.
Example fix
// before Error: backend cleanup refused for unverifiable process pid 4242 ...: root identity mismatch (record has "abc123", workspace has "def456") // after $ ps -p 4242 # confirm not yours / already dead $ mv /ws/.beads/dbproxy/dolt-backend.pid /ws/.beads/dbproxy/dolt-backend.pid.stale-$(date +%s) $ bd doctor
Defensive patterns
Strategy: validation
Validate before calling
// Before cleanup, verify the record belongs to this workspace:
expected, _ := identity.RootID(rootDir)
var rec struct{ RootID string }
if data, err := os.ReadFile(recordPath); err == nil && json.Unmarshal(data, &rec) == nil && rec.RootID != expected {
os.Rename(recordPath, recordPath+".stale-"+fmt.Sprint(time.Now().Unix())) // quarantine foreign record
} Type guard
func recordBelongsToWorkspace(recRootID, wsRootID string) bool { return recRootID == wsRootID } Try / catch
var ule *unverifiableLifecycleError
if err := cleanupOrphanBackend(rootDir); errors.As(err, &ule) && strings.Contains(ule.Error(), "root identity mismatch") {
// foreign record: confirm recorded pid is not ours, then mv record to .stale-<ts>
return nil
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- resolve workspace identity: %w
- root identity mismatch (record has %q, workspace has %q)
- record has no valid pid
- read backend record %s: %w
- quarantine dead unverifiable backend record: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/1d2f52b9cc6b3014.
Report an issue: GitHub.