gastownhall/beads · warning

proxy.ForceStopUnverified: refusing to signal pid %d from %s

Error message

proxy.ForceStopUnverified: refusing to signal pid %d from %s: executable basename is %q, want bd or dolt

What it means

ForceStopUnverified only signals processes whose executable basename is exactly bd or dolt after normalization. The PID from the unverified record resolved to a different binary, so bd refuses to send a signal — the PID was recycled or the record was forged/stale and now points at an unrelated process. This is a deliberate kill-safety guard to prevent killing innocent processes.

Source

Thrown at internal/storage/dbproxy/proxy/force_stop.go:217

	}
	if gone {
		report.ProcessWasGone = true
		return nil
	}
	defer proc.close()

	executable, gone, err := proc.executableBasename()
	if err != nil {
		return fmt.Errorf("proxy.ForceStopUnverified: inspect executable for pid %d: %w", pid, err)
	}
	if gone {
		report.ProcessWasGone = true
		return nil
	}
	executable = normalizeForceStopExecutable(executable)
	report.Executable = executable
	if executable != "bd" && executable != "dolt" {
		return fmt.Errorf(
			"proxy.ForceStopUnverified: refusing to signal pid %d from %s: executable basename is %q, want bd or dolt",
			pid,
			report.RecordPath,
			executable,
		)
	}

	// Basename alone would let a recycled PID now running an unrelated bd or
	// dolt be killed; require the command line to tie the process to THIS
	// workspace, and refuse when that scope cannot be established.
	scoped, gone, err := proc.commandLineContains(rootDir)
	if err != nil {
		return fmt.Errorf(
			"proxy.ForceStopUnverified: refusing to signal pid %d from %s: workspace scope could not be established (%v); stop the process manually, then quarantine the record by renaming %s to %s.stale-<unix-timestamp> before retrying",
			pid,
			report.RecordPath,
			err,
			report.RecordPath,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify with ps -p <pid> -o comm= what the process actually is; if it is not yours, do not kill it
  2. Quarantine the stale record: rename <record> to <record>.stale-<unix-timestamp>, then retry force-stop
  3. If the process is yours and unrelated to bd, stop it manually yourself
  4. Regenerate the workspace state so a fresh, correct PID record is written

Example fix

// before (manually deleting or ignoring the refusal)
// rm .bd/bd.pid && bd doctor --fix
// after: quarantine instead of delete so history is auditable
mv .bd/bd.pid .bd/bd.pid.stale-$(date +%s)
bd doctor --fix
Defensive patterns

Strategy: validation

Validate before calling

// before force-stop, confirm the recorded pid still runs bd/dolt
pid := readPidFromRecord(recordPath)
comm, _ := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid))
name := strings.TrimSpace(string(comm))
if name != "bd" && name != "dolt" {
    os.Rename(recordPath, recordPath+".stale-"+fmt.Sprint(time.Now().Unix())) // recycled pid
}

Type guard

func isExecutableMismatchErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "executable basename is")
}

Prevention

When it happens

Trigger: Calling proxy.ForceStopUnverified with a PID record whose PID has been recycled by the OS and reassigned to a non-bd/non-dolt process (e.g. sshd, python), or a record that was hand-edited/copied and never pointed at bd or dolt.

Common situations: Long-lived machines where PIDs wrap around and stale .pid files in a workspace refer to reused PIDs; containers restarted with different PID assignments; users copying pid files between workspaces; upgrading such that a different daemon now owns the port.

Related errors


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