go-delve/delve · error
could not deliver signal
Error message
could not deliver signal
What it means
On Linux, after the stop check passes, kill() sends SIGKILL to the entire process group via sys.Kill(-dbp.pid, SIGKILL). This error wraps any failure from that syscall (e.g. ESRCH when the group no longer exists, EPERM when lacking permission), prefixed with "could not deliver signal ". Note the stored message here has an empty suffix string in the error constant description, but at runtime the errno text is appended.
Source
Thrown at pkg/proc/native/proc_linux.go:313
}
func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
if dbp.os.ebpf == nil {
return nil
}
return dbp.os.ebpf.GetBufferedTracepoints()
}
// kill kills the target process.
func (procgrp *processGroup) kill(dbp *nativeProcess) error {
if ok, _ := dbp.Valid(); !ok {
return nil
}
if !dbp.threads[dbp.pid].Stopped() {
return errors.New("process must be stopped in order to kill it")
}
if err := sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
return errors.New("could not deliver signal " + err.Error())
}
// wait for other threads first or the thread group leader (dbp.pid) will never exit.
for threadID := range dbp.threads {
if threadID != dbp.pid {
dbp.wait(threadID, 0)
}
}
for {
wpid, status, err := dbp.wait(dbp.pid, 0)
if err != nil {
return err
}
if wpid == dbp.pid && status != nil && status.Signaled() && status.Signal() == sys.SIGKILL {
dbp.postExit()
return err
}
}
}View on GitHub (pinned to a23773e6c3)
Solutions
- Check whether the process still exists; ESRCH usually means it already died — treat as success and clean up.
- Run delve with sufficient privileges (same user or root/CAP_KILL) if EPERM.
- Retry the operation; stop/exit races are transient.
- Verify process-group setup (setpgid) if launching delve in unusual environments (CI, containers).
Defensive patterns
Strategy: try-catch
Validate before calling
// check the process group still exists before killing
if err := syscall.Kill(-pid, 0); err != nil {
// group gone; process already exited
} Type guard
func isCouldNotDeliverSignal(err error) bool {
return err != nil && strings.Contains(err.Error(), "could not deliver signal")
} Try / catch
if err := dlv.Kill(); err != nil {
var se syscall.Errno
if strings.Contains(err.Error(), "could not deliver signal") && errors.As(err, &se) && se == syscall.ESRCH {
return nil // already dead
}
return err
} Prevention
- Treat ESRCH-wrapped failures as benign (process already exited).
- Ensure delve runs with permission to signal the target (same user/root).
- Verify process-group setup in containers and CI (setpgid).
- Avoid double-kill flows that race with target exit.
When it happens
Trigger: kill() invoking sys.Kill(-dbp.pid, sys.SIGKILL) which returns an error — the negative-pid group kill fails because the process group vanished (ESRCH), or the caller lacks permission (EPERM), or the pid is not a process-group leader.
Common situations: Target already exited between the stop check and the kill (classic race); killing processes in containers lacking CAP_KILL; session/Process-group differences after attach to a non-group-leader.
Related errors
- could not deliver signal:
- process must be stopped in order to kill it
- no eBPF program loaded
- eBPF map not loaded
- number of loaded libraries exceeds maximum
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/19310d918c2dd164.
Report an issue: GitHub.