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

  1. Check whether the process still exists; ESRCH usually means it already died — treat as success and clean up.
  2. Run delve with sufficient privileges (same user or root/CAP_KILL) if EPERM.
  3. Retry the operation; stop/exit races are transient.
  4. 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

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


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/19310d918c2dd164. Report an issue: GitHub.