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

  1. Re-run the command — if the process exited in a race, the next attempt will take the 'gone' path
  2. Run with sufficient privileges (or as the same user who owns the proxy process) so the OS permits opening the target
  3. Check the wrapped cause (%w): EPERM/EACCES means permissions; ENOENT usually means the process exited and a retry suffices
  4. 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

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


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