go-delve/delve · error
process must be stopped in order to kill it
Error message
process must be stopped in order to kill it
What it means
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.
Source
Thrown at pkg/proc/native/proc_linux.go:310
dbp.os.comm = strings.ReplaceAll(string(comm), "%", "%%")
return getCmdLine(dbp.pid), nil
}
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 errView on GitHub (pinned to a23773e6c3)
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.
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the process is stopped before killing: halt via the debugger first
dlv.Halt() // or send Ctrl-C / SIGINT, then wait for stopped state
if !isStopped(dlv) {
return errors.New("halt target before kill")
} Type guard
func isMustBeStoppedErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "process must be stopped")
} Try / catch
if err := dlv.Kill(); err != nil {
if strings.Contains(err.Error(), "process must be stopped") {
dlv.Halt()
return dlv.Kill()
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- could not deliver signal
- waiting for target execve failed: %s
- could not attach to new thread %d %s
- thread already exited %d
- error while waiting after adding thread: %d %s
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/b8f0d8c0cbabadab.
Report an issue: GitHub.