{"record":{"id":"fea811b9b2a62e24","repo":"gastownhall/beads","slug":"procid-pidfd-signal-d-w","errorCode":null,"errorMessage":"procid: pidfd signal %d: %w","messagePattern":"procid: pidfd signal (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/procid/procid_linux.go","lineNumber":110,"sourceCode":"\t\treturn nil, fmt.Errorf(\"procid: process %d does not match token\", pid)\n\t}\n\treturn &Handle{pid: pid, token: tok, pidfd: fd}, nil\n}\n\n// Signal sends sig to the verified process.\nfunc (h *Handle) Signal(sig os.Signal) error {\n\tif h.pidfd >= 0 {\n\t\tunixSig, ok := sig.(syscall.Signal)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"procid: unsupported signal %v\", sig)\n\t\t}\n\t\tif err := unix.PidfdSendSignal(h.pidfd, unixSig, nil, 0); err != nil {\n\t\t\tif isFatalSignal(unixSig) && errors.Is(err, unix.ESRCH) {\n\t\t\t\t// The target exited on its own after Open; a fatal signal's\n\t\t\t\t// goal is already met, matching the fallback path.\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"procid: pidfd signal %d: %w\", h.pid, err)\n\t\t}\n\t\treturn nil\n\t}\n\tif err := h.verifyThenSignal(sig); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Kill sends SIGKILL to the verified process.\nfunc (h *Handle) Kill() error { return h.Signal(syscall.SIGKILL) }\n\n// Close releases the underlying pidfd, if one was opened.\nfunc (h *Handle) Close() error {\n\tif h.pidfd < 0 {\n\t\treturn nil\n\t}\n\terr := unix.Close(h.pidfd)","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/procid/procid_linux.go#L92-L128","documentation":"On the Linux pidfd path, this wraps the errno returned by PidfdSendSignal when it fails and the error is not the swallowed fatal-signal+ESRCH case. Common causes: ESRCH for non-fatal signals (process exited after Open), EPERM (blocked by seccomp or lacking permission), EINVAL, or EMFILE-adjacent fd issues on the pidfd itself.","triggerScenarios":"Handle.Signal on a pidfd-backed Linux handle where unix.PidfdSendSignal returns an error other than fatal-signal+ESRCH: sending a non-fatal signal (e.g. SIGUSR1) to a process that already exited (ESRCH); seccomp-blocked pidfd_send_signal (EPERM/ENOSYS at syscall layer); invalid pidfd after Close was called.","commonSituations":"Signaling a process that exited between Open and Signal with a non-fatal signal; containers with restrictive seccomp profiles lacking pidfd_send_signal; reusing a Handle after calling Close (closed pidfd → EBADF).","solutions":["Check errors.Is(err, unix.ESRCH) and treat the target as gone for non-fatal signals; reconcile state.","Check errors.Is(err, unix.EPERM)/ENOSYS: update seccomp/container profile to allow pidfd_send_signal, or rely on the fallback path.","Do not use a Handle after Close; reopen with procid.Open.","Check errors.Is(err, unix.EBADF): the pidfd was closed — reacquire the handle."],"exampleFix":"// before\nh.Close()\n_ = h.Signal(syscall.SIGUSR1) // procid: pidfd signal 4242: bad file descriptor\n// after\nif err := h.Signal(syscall.SIGUSR1); err != nil {\n    switch {\n    case errors.Is(err, unix.ESRCH):\n        // target already exited\n    case errors.Is(err, unix.EBADF):\n        h, err = procid.Open(h_pid, tok) // reopen handle\n    }\n}","handlingStrategy":"try-catch","validationCode":"if h.PidfdActive() { /* ensure not Closed before Signal */ }\n// Probe target liveness for non-fatal signals:\nmatch, err := procid.Verify(h_pid, tok)\nif err != nil || !match { return /* gone; skip non-fatal signal */ }","typeGuard":null,"tryCatchPattern":"if err := h.Signal(syscall.SIGUSR1); err != nil {\n    if strings.Contains(err.Error(), \"pidfd signal\") {\n        if errors.Is(err, unix.ESRCH) {\n            return // exited after Open\n        }\n        if errors.Is(err, unix.EPERM) || errors.Is(err, unix.ENOSYS) {\n            // seccomp blocks pidfd_send_signal: fix profile or fall back\n        }\n        if errors.Is(err, unix.EBADF) {\n            // handle Closed: reopen via procid.Open\n        }\n    }\n    return err\n}","preventionTips":["Never use a Handle after Close; reopen with procid.Open","Ensure container seccomp profiles allow pidfd_send_signal","For non-fatal signals, Verify the process is alive right before signaling","Classify wrapped errnos with errors.Is rather than string matching where possible"],"tags":["linux","pidfd","signal","seccomp"],"backgroundTag":"pidfd-signal-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}