{"record":{"id":"19310d918c2dd164","repo":"go-delve/delve","slug":"could-not-deliver-signal-19310d","errorCode":null,"errorMessage":"could not deliver signal ","messagePattern":"could not deliver signal ","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/native/proc_linux.go","lineNumber":313,"sourceCode":"}\n\nfunc (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {\n\tif dbp.os.ebpf == nil {\n\t\treturn nil\n\t}\n\treturn dbp.os.ebpf.GetBufferedTracepoints()\n}\n\n// kill kills the target process.\nfunc (procgrp *processGroup) kill(dbp *nativeProcess) error {\n\tif ok, _ := dbp.Valid(); !ok {\n\t\treturn nil\n\t}\n\tif !dbp.threads[dbp.pid].Stopped() {\n\t\treturn errors.New(\"process must be stopped in order to kill it\")\n\t}\n\tif err := sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {\n\t\treturn errors.New(\"could not deliver signal \" + err.Error())\n\t}\n\t// wait for other threads first or the thread group leader (dbp.pid) will never exit.\n\tfor threadID := range dbp.threads {\n\t\tif threadID != dbp.pid {\n\t\t\tdbp.wait(threadID, 0)\n\t\t}\n\t}\n\tfor {\n\t\twpid, status, err := dbp.wait(dbp.pid, 0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif wpid == dbp.pid && status != nil && status.Signaled() && status.Signal() == sys.SIGKILL {\n\t\t\tdbp.postExit()\n\t\t\treturn err\n\t\t}\n\t}\n}","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/native/proc_linux.go#L295-L331","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check the process group still exists before killing\nif err := syscall.Kill(-pid, 0); err != nil {\n\t// group gone; process already exited\n}","typeGuard":"func isCouldNotDeliverSignal(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"could not deliver signal\")\n}","tryCatchPattern":"if err := dlv.Kill(); err != nil {\n\tvar se syscall.Errno\n\tif strings.Contains(err.Error(), \"could not deliver signal\") && errors.As(err, &se) && se == syscall.ESRCH {\n\t\treturn nil // already dead\n\t}\n\treturn err\n}","preventionTips":["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."],"tags":["linux","signal","sigkill","debugging"],"backgroundTag":"signal-delivery-failed","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}