gastownhall/beads · error
signal pid %d: %w
Error message
signal pid %d: %w
What it means
unverifiedProcess.kill on macOS sends SIGKILL to the recorded PID. ESRCH (process already gone) is treated as success; any other signal error is wrapped in this error. The library refuses to silently continue when it cannot confirm the target was killed, since the PID's identity was never fully verified.
Source
Thrown at internal/storage/dbproxy/proxy/unverified_process_darwin.go:55
// needle, read best-effort through ps.
func (p *unverifiedProcess) commandLineContains(needle string) (matched bool, gone bool, err error) {
output, commandErr := exec.Command("ps", "-p", strconv.Itoa(p.pid), "-o", "args=").Output()
if commandErr != nil {
if killErr := syscall.Kill(p.pid, 0); errors.Is(killErr, unix.ESRCH) {
return false, true, nil
}
return false, false, fmt.Errorf("read command line for pid %d: %w", p.pid, commandErr)
}
return strings.Contains(strings.TrimSpace(string(output)), needle), false, nil
}
// kill sends SIGKILL. gone reports a target that had already exited.
func (p *unverifiedProcess) kill() (gone bool, err error) {
if killErr := syscall.Kill(p.pid, syscall.SIGKILL); killErr != nil {
if errors.Is(killErr, unix.ESRCH) {
return true, nil
}
return false, fmt.Errorf("signal pid %d: %w", p.pid, killErr)
}
return false, nil
}
func (p *unverifiedProcess) exited() (bool, error) {
_, gone, err := processExecutableBasename(p.pid)
if err != nil {
return false, err
}
return gone, nil
}
func (p *unverifiedProcess) close() {}
View on GitHub (pinned to 71377f2769)
Solutions
- Re-run with sufficient privileges (sudo) if EPERM is the underlying cause, after manually confirming the PID's identity with `ps -p <pid> -o args=`.
- Verify with `ps -p <pid>` whether the PID was recycled to an unrelated process; if so, do NOT kill it — remove the stale pidfile instead.
- Check sandbox/container policies that block signals and adjust them.
- Restart the terminal session/daemon cleanly so a fresh pidfile with a live PID is written.
Example fix
// before: EPERM killing other-user process sudo kill -9 <pid> # only after verifying identity // after: stale record cleaned rm .beads/bd.pid && bd ...
Defensive patterns
Strategy: try-catch
Validate before calling
out, err := exec.Command("ps", "-o", "user=", "-p", strconv.Itoa(pid)).Output()
if err == nil && strings.TrimSpace(string(out)) != currentUser {
// PID likely recycled to another user: do not signal
} Try / catch
gone, err := proc.kill()
if err != nil {
var errno syscall.Errno
if errors.As(err, &errno) && errno == syscall.EPERM {
// verify identity manually; never blind-sudo a recycled PID
}
return err
} Prevention
- Run the stop command as the same user that started the daemon.
- Verify a PID's identity (ps -p <pid> -o args=,user=) before elevating privileges to kill it.
- Clean up stale pidfiles after abnormal daemon exits so old PIDs are not reused.
- Be extra careful on long-lived systems where PID wraparound/recycling is likely.
When it happens
Trigger: kill() calls syscall.Kill(pid, SIGKILL) and receives an error other than ESRCH — e.g. EPERM (owned by another user), or the PID was recycled into a protected system process.
Common situations: The stale daemon PID now belongs to a root-owned or another-user process (PID recycling); running without sufficient privileges in a container; macOS seatbelt/sandbox blocking signals.
Related errors
- pidfd signal %d: %w
- signal pid %d: %w
- procid: unsupported signal %v
- procid: signal %d: %w
- procid: process %d still matches token after fatal signal an
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dfbbadd5a73be535.
Report an issue: GitHub.