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 err

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Stop the process first (halt/Ctrl-C or continue until a stop) before killing.
  2. Retry the kill after the target reports stopped state.
  3. Check the target hasn't already exited (`ps -p <pid>`) — if it exited, nothing to kill.
  4. 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

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


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