{"record":{"id":"b8f0d8c0cbabadab","repo":"go-delve/delve","slug":"process-must-be-stopped-in-order-to-kill-it","errorCode":null,"errorMessage":"process must be stopped in order to kill it","messagePattern":"process must be stopped in order to kill it","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/native/proc_linux.go","lineNumber":310,"sourceCode":"\tdbp.os.comm = strings.ReplaceAll(string(comm), \"%\", \"%%\")\n\n\treturn getCmdLine(dbp.pid), nil\n}\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","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/native/proc_linux.go#L292-L328","documentation":"On Linux, the processGroup.kill method refuses to kill a target whose main thread (thread group leader) is not in a stopped (ptrace-stopped) state. Delivering SIGKILL and reaping threads requires the tracee to be stopped so ptrace state is consistent. This guard prevents killing a process that isn't under proper ptrace control at that moment.","triggerScenarios":"Calling Kill (directly or via `kill` command / Detach-kill flows) when dbp.threads[dbp.pid].Stopped() is false — i.e. the main thread is running, just signaled, or mid-exit instead of ptrace-stopped.","commonSituations":"Issuing `kill` in the CLI while the target is running (not halted at a breakpoint); racing the target's exit; attach sessions where the initial stop hasn't fully propagated to the leader thread.","solutions":["Stop the process first (halt/Ctrl-C or continue until a stop) before killing.","Retry the kill after the target reports stopped state.","Check the target hasn't already exited (`ps -p <pid>`) — if it exited, nothing to kill.","Update delve; stop-state races around kill have been addressed in fixes to the linux backend."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure the process is stopped before killing: halt via the debugger first\ndlv.Halt() // or send Ctrl-C / SIGINT, then wait for stopped state\nif !isStopped(dlv) {\n\treturn errors.New(\"halt target before kill\")\n}","typeGuard":"func isMustBeStoppedErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"process must be stopped\")\n}","tryCatchPattern":"if err := dlv.Kill(); err != nil {\n\tif strings.Contains(err.Error(), \"process must be stopped\") {\n\t\tdlv.Halt()\n\t\treturn dlv.Kill()\n\t}\n\treturn err\n}","preventionTips":["Always halt (Ctrl-C / Halt RPC) before issuing kill.","Check process state via the debugger API rather than assuming it's stopped.","Avoid killing during startup races right after Launch.","Watch for the target exiting on its own — then kill is unnecessary."],"tags":["linux","ptrace","process-kill","debugging"],"backgroundTag":"process-not-stopped","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}