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

  1. 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=`.
  2. 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.
  3. Check sandbox/container policies that block signals and adjust them.
  4. 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

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


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