gastownhall/beads · error

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

Error message

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

What it means

After confirming the executable is bd or dolt, ForceStopUnverified requires the process command line to reference the current workspace root before signaling. If the command line cannot be read or compared (the commandLineContains check errored), bd cannot prove the process belongs to this workspace and refuses to kill it, since a recycled PID running an unrelated bd/dolt could otherwise be terminated.

Source

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

		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,
			report.RecordPath,
		)
	}
	if gone {
		report.ProcessWasGone = true
		return nil
	}
	if !scoped {
		return fmt.Errorf(
			"proxy.ForceStopUnverified: refusing to signal pid %d from %s: its command line does not reference workspace %s, so it may be an unrelated %s process; stop it manually if it is yours, then quarantine the record by renaming %s to %s.stale-<unix-timestamp> before retrying",
			pid,
			report.RecordPath,
			rootDir,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Stop the process manually (kill <pid> as the owning user or inside its container)
  2. Quarantine the record: rename <record> to <record>.stale-<unix-timestamp>, then retry the force-stop
  3. Re-run inside the same user/session/container where the daemon runs so the cmdline is readable
  4. Check procfs availability and permissions (ls -l /proc/<pid>/cmdline) and adjust security policy if it blocks reads

Example fix

// before: retrying force-stop unchanged fails forever
bd daemon --force-stop
// after
kill <pid>            # stop manually as the owning user
mv .bd/bd.pid .bd/bd.pid.stale-$(date +%s)
bd daemon --force-stop # now succeeds or reports process gone
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm cmdline of the pid is readable and references the workspace first
pid := readPidFromRecord(recordPath)
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
if err != nil || !strings.Contains(strings.ReplaceAll(string(cmdline), "\x00", " "), rootDir) {
    // cannot establish scope: stop manually and quarantine before calling
    os.Rename(recordPath, recordPath+".stale-"+fmt.Sprint(time.Now().Unix()))
}

Type guard

func isScopeUnestablishableErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "workspace scope could not be established")
}

Try / catch

err := proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)
if isScopeUnestablishableErr(err) {
    // follow the remediation in the message: manual kill + quarantine, then retry
    return fmt.Errorf("stop pid manually, rename %s to %s.stale-<ts>, retry", recordPath, recordPath)
}

Prevention

When it happens

Trigger: proxy.ForceStopUnverified when reading /proc/<pid>/cmdline (or platform equivalent) fails: permission denied on another user's process, cmdline unavailable in the container/namespace, procfs not mounted, or a transient kernel error while the process is exiting.

Common situations: bd/dolt daemon started under a different user or in a different container than the bd client attempting force-stop; hardened environments blocking cmdline reads; ephemeral runners where procfs access is restricted.

Related errors


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