{"record":{"id":"4587ebe25330928e","repo":"gastownhall/beads","slug":"pidfd-signal-d-w","errorCode":null,"errorMessage":"pidfd signal %d: %w","messagePattern":"pidfd signal (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/unverified_process_linux.go","lineNumber":74,"sourceCode":"\t\treturn false, false, fmt.Errorf(\"read cmdline for pid %d: %w\", p.pid, err)\n\t}\n\tif len(data) == 0 {\n\t\t// Zombies expose an empty cmdline; the process has effectively exited.\n\t\treturn false, true, nil\n\t}\n\tcmdline := strings.ReplaceAll(strings.TrimRight(string(data), \"\\x00\"), \"\\x00\", \" \")\n\treturn strings.Contains(cmdline, needle), false, nil\n}\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","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/unverified_process_linux.go#L56-L92","documentation":"unverifiedProcess.kill on Linux prefers the pidfd-stable signal path: PidfdSendSignal(pidfd, SIGKILL) targets the exact process the handle refers to, immune to PID recycling. ESRCH means the process already exited (treated as success); any other error is wrapped in this error and the stop sequence aborts rather than risking an unverified kill.","triggerScenarios":"kill() with pidfd >= 0 calls unix.PidfdSendSignal and gets an error other than ESRCH — e.g. EPERM (no permission for the target), EINVAL (bad flags), or the pidfd became invalid.","commonSituations":"Killing a process owned by another user (EPERM) after PID namespace changes in containers; seccomp blocking pidfd_send_signal; kernel quirks with pidfd on very new/patched kernels.","solutions":["Re-run with elevated privileges (sudo) after manually verifying the PID identity via /proc/<pid>/cmdline.","Check seccomp/container profile allows pidfd_send_signal.","Close stale pidfds / retry the stop operation so a fresh pidfd is opened.","As a last resort, kill by verified PID manually and delete the stale pidfile."],"exampleFix":"// before: EPERM in container for cross-user pidfd signal\nsudo bd ...\n// after: stop succeeds with privileges\n# pidfile cleaned, daemon stopped","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"gone, err := proc.kill()\nif err != nil {\n    if errors.Is(err, unix.EPERM) {\n        // target owned by another user/namespace: verify manually before escalating\n    }\n    return err\n}","preventionTips":["Stop daemons from the same user account (and container/namespace) that started them.","Allow pidfd_send_signal in seccomp/container profiles.","Verify /proc/<pid>/cmdline identity before escalating privileges on EPERM.","Keep kernel and libseccomp/golang.org/x/sys versions current."],"tags":["go","linux","pidfd","sigkill","signal"],"backgroundTag":"process-kill-signal-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}