{"record":{"id":"121238957d1d386d","repo":"gastownhall/beads","slug":"procid-unsupported-signal-v-121238","errorCode":null,"errorMessage":"procid: unsupported signal %v","messagePattern":"procid: unsupported signal (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/procid/procid_linux.go","lineNumber":102,"sourceCode":"\t}\n\tmatch, verifyErr := Verify(pid, tok)\n\tif verifyErr != nil {\n\t\t_ = unix.Close(fd)\n\t\treturn nil, verifyErr\n\t}\n\tif !match {\n\t\t_ = unix.Close(fd)\n\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.","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/procid/procid_linux.go#L84-L120","documentation":"Handle.Signal on the Linux pidfd path only accepts signals of concrete type syscall.Signal, because PidfdSendSignal needs a raw signal number. Any other os.Signal implementation (custom wrapper, foreign enum) fails this check and nothing is sent.","triggerScenarios":"Calling Handle.Signal on a Linux handle with pidfd >= 0 passing a value that implements os.Signal but is not syscall.Signal (custom signal type, mock, or signal from a non-syscall package).","commonSituations":"Wrapping signals in an app-level type for logging; passing os.Interral-like constants redefined locally; passing test doubles in unit tests; accidentally passing nil cast to os.Signal.","solutions":["Pass syscall.Signal constants (syscall.SIGTERM, syscall.SIGKILL, etc.).","Convert with an explicit cast: syscall.Signal(sigNum) before calling Signal.","Centralize signal construction in one helper that always returns syscall.Signal."],"exampleFix":"// before\ntype appSignal string\nfunc (a appSignal) Signal() {}\nvar s os.Signal = appSignal(\"kill\")\nh.Signal(s) // unsupported signal\n// after\nh.Signal(syscall.SIGKILL)","handlingStrategy":"type-guard","validationCode":"sig, ok := desired.(syscall.Signal)\nif !ok {\n    return fmt.Errorf(\"need syscall.Signal, got %T\", desired)\n}","typeGuard":"func isSyscallSignal(s os.Signal) (syscall.Signal, bool) {\n    ss, ok := s.(syscall.Signal)\n    return ss, ok\n}","tryCatchPattern":"if err := h.Signal(sig); err != nil {\n    if strings.Contains(err.Error(), \"unsupported signal\") {\n        return fmt.Errorf(\"signal %T must be syscall.Signal\", sig)\n    }\n    return err\n}","preventionTips":["Use only syscall.SIG* constants with Handle.Signal","Convert cross-platform signal abstractions to syscall.Signal at call boundaries","Reject custom os.Signal implementations in code review","Add a type assertion test for any signal-producing helper"],"tags":["signal","type-mismatch","linux","pidfd"],"backgroundTag":"unsupported-signal-type","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}