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
A hard mismatch check during pidfile kill-record validation: the daemon recorded a workspace RootID that no longer matches the RootID computed for the current root directory. The proxy refuses to kill the recorded process because it may belong to a different workspace, preventing cross-workspace process kills.
Source
Thrown at internal/storage/dbproxy/proxy/shutdown.go:380
func validateKillRecord(
rootDir string,
pf *pidfile.PidFile,
wantKind string,
checks killRecordChecks,
) error {
if err := pf.ValidateV2(wantKind); err != nil {
return err
}
if !checks.VerifyRoot {
return nil
}
rootID, err := identity.RootID(rootDir)
if err != nil {
return fmt.Errorf("resolve workspace identity: %w", err)
}
if pf.RootID != rootID {
return fmt.Errorf("root identity mismatch (record has %q, workspace has %q)", pf.RootID, rootID)
}
return nil
}
func classifyInvalidKillRecord(pf *pidfile.PidFile, validationErr error) (dead bool, live bool, err error) {
if !isPIDFileValidationError(validationErr) {
return false, false, nil
}
return probeUnverifiablePID(pf.Pid)
}
func isPIDFileValidationError(err error) bool {
return errors.Is(err, pidfile.ErrLegacySchema) ||
errors.Is(err, pidfile.ErrBadPid) ||
errors.Is(err, pidfile.ErrBadPort) ||
errors.Is(err, pidfile.ErrKindMismatch) ||
errors.Is(err, pidfile.ErrMissingBirth)
}View on GitHub (pinned to 71377f2769)
Solutions
- Kill the stale daemon manually (from the pidfile PID) and delete the pidfile so a fresh one with the current RootID is written.
- Confirm you are in the same physical directory the daemon was started in; cd back to the original workspace path.
- Recreate the workspace metadata (bd doctor / reinitialize) so identity.RootID matches what the daemon recorded.
- Copy the original workspace back to its original path if it was moved.
Example fix
// before: stale pidfile with mismatched RootID kill $(cat .beads/bd.pid); rm .beads/bd.pid // after: restart in correct workspace bd ... # new pidfile written with current RootID
Defensive patterns
Strategy: validation
Validate before calling
pf, err := pidfile.Read(path)
if err != nil { return err }
rootID, err := identity.RootID(rootDir)
if err != nil { return err }
if pf.RootID != rootID {
// stale record from another workspace: clean up manually, never kill
return nil
} Try / catch
if err := stopAndAcquire(ctx); err != nil {
if strings.Contains(err.Error(), "root identity mismatch") {
// treat pidfile as stale: remove it manually instead of killing the PID
}
} Prevention
- Always run bd from the same directory the daemon was started in.
- Avoid cloning/copying a workspace that contains a live pidfile; remove .beads pidfiles before copying.
- One daemon per workspace path — do not share workspace directories across machines or containers.
- After moving a project, stop the daemon first, then move, then restart.
When it happens
Trigger: stopAndAcquire -> validateKillRecord compares pf.RootID (stored in the pidfile) against identity.RootID(rootDir); any inequality raises this error — typically after the workspace directory was moved, copied, or the root was recreated.
Common situations: Cloning/moving a project directory while a daemon from the original location is still running; running two workspaces that share an inherited pidfile path; restoring the repo from a backup that changed identity metadata.
Related errors
- root identity mismatch (record has %q, workspace has %q)
- resolve workspace identity: %w
- 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/53de1e850e660dbe.
Report an issue: GitHub.