gastownhall/beads · error
procid: unsupported signal %v
Error message
procid: unsupported signal %v
What it means
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.
Source
Thrown at internal/procid/procid_linux.go:102
}
match, verifyErr := Verify(pid, tok)
if verifyErr != nil {
_ = unix.Close(fd)
return nil, verifyErr
}
if !match {
_ = unix.Close(fd)
return nil, fmt.Errorf("procid: process %d does not match token", pid)
}
return &Handle{pid: pid, token: tok, pidfd: fd}, nil
}
// Signal sends sig to the verified process.
func (h *Handle) Signal(sig os.Signal) error {
if h.pidfd >= 0 {
unixSig, ok := sig.(syscall.Signal)
if !ok {
return fmt.Errorf("procid: unsupported signal %v", sig)
}
if err := unix.PidfdSendSignal(h.pidfd, unixSig, nil, 0); err != nil {
if isFatalSignal(unixSig) && errors.Is(err, unix.ESRCH) {
// The target exited on its own after Open; a fatal signal's
// goal is already met, matching the fallback path.
return nil
}
return fmt.Errorf("procid: pidfd signal %d: %w", h.pid, err)
}
return nil
}
if err := h.verifyThenSignal(sig); err != nil {
return err
}
return nil
}
// Kill sends SIGKILL to the verified process.View on GitHub (pinned to 71377f2769)
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.
Example fix
// before
type appSignal string
func (a appSignal) Signal() {}
var s os.Signal = appSignal("kill")
h.Signal(s) // unsupported signal
// after
h.Signal(syscall.SIGKILL) Defensive patterns
Strategy: type-guard
Validate before calling
sig, ok := desired.(syscall.Signal)
if !ok {
return fmt.Errorf("need syscall.Signal, got %T", desired)
} Type guard
func isSyscallSignal(s os.Signal) (syscall.Signal, bool) {
ss, ok := s.(syscall.Signal)
return ss, ok
} Try / catch
if err := h.Signal(sig); err != nil {
if strings.Contains(err.Error(), "unsupported signal") {
return fmt.Errorf("signal %T must be syscall.Signal", sig)
}
return err
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- procid: pidfd signal %d: %w
- pidfd signal %d: %w
- procid: unsupported signal %v
- procid: pidfd open %d: %w
- procid: process %d still matches token after fatal signal an
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/121238957d1d386d.
Report an issue: GitHub.