{"record":{"id":"5e6e73ebfa183427","repo":"gastownhall/beads","slug":"signal-pid-d-w-5e6e73","errorCode":null,"errorMessage":"signal pid %d: %w","messagePattern":"signal pid (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/unverified_process_linux.go","lineNumber":82,"sourceCode":"}\n\n// kill sends SIGKILL through the held handle. gone reports a target that had\n// already exited.\nfunc (p *unverifiedProcess) kill() (gone bool, err error) {\n\tif p.pidfd >= 0 {\n\t\tif err := unix.PidfdSendSignal(p.pidfd, unix.SIGKILL, nil, 0); err != nil {\n\t\t\tif errors.Is(err, unix.ESRCH) {\n\t\t\t\treturn true, nil\n\t\t\t}\n\t\t\treturn false, fmt.Errorf(\"pidfd signal %d: %w\", p.pid, err)\n\t\t}\n\t\treturn false, nil\n\t}\n\tif err := syscall.Kill(p.pid, syscall.SIGKILL); err != nil {\n\t\tif errors.Is(err, unix.ESRCH) {\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"signal pid %d: %w\", p.pid, err)\n\t}\n\treturn false, nil\n}\n\n// exited reports whether the process is gone (or reduced to a zombie). While\n// the pidfd is held the PID cannot be recycled, so a /proc probe is stable.\nfunc (p *unverifiedProcess) exited() (bool, error) {\n\t_, gone, err := processExecutableBasename(p.pid)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn gone, nil\n}\n\nfunc (p *unverifiedProcess) close() {\n\tif p.pidfd >= 0 {\n\t\t_ = unix.Close(p.pidfd)\n\t\tp.pidfd = -1","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/unverified_process_linux.go#L64-L100","documentation":"Fallback path of unverifiedProcess.kill on Linux (used when pidfd is unavailable, e.g. ENOSYS on old kernels): plain syscall.Kill(pid, SIGKILL). ESRCH is success (already gone); any other error is wrapped here. Without a pidfd, the PID is not protected from recycling, so this error means the proxy must stop rather than risk signaling an unrelated process.","triggerScenarios":"kill() with pidfd == -1 calls syscall.Kill(pid, SIGKILL) and gets a non-ESRCH error — typically EPERM (process owned by another UID, e.g. recycled PID) or EPERM from LSM policy.","commonSituations":"Old kernels without pidfd support combined with PID recycling to another user's process; restricted containers where signaling is blocked; running bd as a different user than the daemon.","solutions":["Verify the PID's identity with `cat /proc/<pid>/cmdline`; if recycled to another user's process, do not kill — just remove the stale pidfile.","Run the stop command as the same user (or with sudo, after identity verification).","Upgrade the kernel to >=5.3 to get the safer pidfd path.","Check LSM/seccomp policies blocking signals and relax them for this operation."],"exampleFix":"// before: recycled PID owned by another user\ncat /proc/<pid>/cmdline  # verify owner/identity first\n// after: only remove stale record, never kill foreign PID\nrm .beads/bd.pid && bd ...","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isSignalErr(err error) (errno syscall.Errno, ok bool) {\n    if errors.As(err, &errno) && (errno == syscall.EPERM || errno == syscall.EACCES) {\n        return errno, true\n    }\n    return 0, false\n}","tryCatchPattern":"gone, err := proc.kill() // pidfd == -1 fallback\nif errno, ok := isSignalErr(err); ok && errno == syscall.EPERM {\n    // PID likely recycled to another user: inspect /proc/<pid>/cmdline, never blind-kill\n    return err\n}","preventionTips":["Prefer kernels >=5.3 so the pidfd (recycle-safe) path is used instead of plain kill.","Check /proc/<pid>/cmdline ownership/identity before any manual kill on EPERM.","Run stop commands as the daemon's owning user.","Reap stale pidfiles promptly after crashes to reduce PID-recycling windows."],"tags":["go","linux","signal","sigkill","eperm"],"backgroundTag":"process-kill-signal-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}