gastownhall/beads · error
proxy.ForceStopUnverified: open pid %d: %w
Error message
proxy.ForceStopUnverified: open pid %d: %w
What it means
This error wraps a failure to open a handle to the unverified process identified by the record PID, so it can be inspected and signaled atomically. The library throws it when openUnverifiedProcess fails (e.g. opening /proc/<pid>, or OpenProcess on Windows fails for reasons other than the process being gone), because without a stable handle the executable check and kill could race with PID reuse.
Source
Thrown at internal/storage/dbproxy/proxy/force_stop.go:198
}
if record.RootID == rootID {
return errors.New(
"proxy.ForceStopUnverified: record has a verifiable v2 workspace identity; use proxy.Shutdown",
)
}
return nil
}
func inspectAndStopUnverifiedPID(rootDir string, pid int, deadline time.Time, report *ForceStopReport) error {
if pid <= 0 {
return fmt.Errorf("proxy.ForceStopUnverified: record %s has invalid pid %d", report.RecordPath, pid)
}
// One stable handle covers inspection and signaling, so the PID cannot be
// recycled between the executable check and the kill on platforms with a
// pinning primitive (Linux pidfd, Windows process handle).
proc, gone, err := openUnverifiedProcess(pid)
if err != nil {
return fmt.Errorf("proxy.ForceStopUnverified: open pid %d: %w", pid, err)
}
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" {View on GitHub (pinned to 71377f2769)
Solutions
- Re-run the command — if the process exited in a race, the next attempt will take the 'gone' path
- Run with sufficient privileges (or as the same user who owns the proxy process) so the OS permits opening the target
- Check the wrapped cause (%w): EPERM/EACCES means permissions; ENOENT usually means the process exited and a retry suffices
- As root (or the owning user), remove the stale record manually after confirming the process is gone
Defensive patterns
Strategy: retry
Validate before calling
func canOpen(pid int) error {
return unix.Faccessat(unix.AT_FDCWD, fmt.Sprintf("/proc/%d", pid), unix.R_OK, 0)
} Try / catch
report, err := proxy.ForceStopUnverified(rootDir)
if err != nil && strings.Contains(err.Error(), "open pid ") {
var perr syscall.Errno
if errors.As(err, &perr) && (errors.Is(perr, syscall.ENOENT) || errors.Is(perr, syscall.ESRCH)) {
// process likely exited in a race; retry once
report, err = proxy.ForceStopUnverified(rootDir)
}
} Prevention
- Run force-stop as the same user that owns the proxy process
- Avoid hardened seccomp/AppArmor profiles that block pidfd_open or /proc reads
- Handle EPERM by elevating privileges only after confirming the target PID
- Treat ENOENT/ESRCH as 'process gone' and retry rather than escalating
When it happens
Trigger: Calling ForceStopUnverified when openUnverifiedProcess(pid) returns an error (not the 'gone' path) — e.g. insufficient privileges to inspect a foreign-owned PID, an OS API failure, or a PID that vanished between existence check and handle creation in a race window.
Common situations: Force-stopping a proxy owned by another user/container UID, restricted hardened environments (seccomp/AppArmor blocking pidfd_open or /proc access), or extremely short-lived processes exiting during the open call.
Related errors
- sending SIGTERM to PID %d: %w
- procid: process %d no longer matches token
- procid: process %d does not match token
- procid: terminate process: %w
- procid: process no longer matches token
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2511127e7c84e3b7.
Report an issue: GitHub.