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

  1. Kill the stale daemon manually (from the pidfile PID) and delete the pidfile so a fresh one with the current RootID is written.
  2. Confirm you are in the same physical directory the daemon was started in; cd back to the original workspace path.
  3. Recreate the workspace metadata (bd doctor / reinitialize) so identity.RootID matches what the daemon recorded.
  4. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/53de1e850e660dbe. Report an issue: GitHub.