go-delve/delve · error

could not stop process %d

Error message

could not stop process %d

What it means

waitForStop gives the debuggee's threads a bounded number of chances (16 iterations) to stop; if threads are still running after that, it gives up with "could not stop process <n>". This indicates the mach task_suspend/thread_suspend path could not quiesce the process, leaving the debugger unable to inspect memory safely.

Source

Thrown at pkg/proc/native/proc_darwin.go:398

}

func (dbp *nativeProcess) waitForStop() ([]int, error) {
	ports := make([]int, 0, len(dbp.threads))
	count := 0
	for {
		var task C.task_t
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
		if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
			count = 0
			ports = append(ports, int(port))
		} else {
			n := C.num_running_threads(dbp.os.task)
			if n == 0 {
				return ports, nil
			} else if n < 0 {
				return nil, fmt.Errorf("error waiting for thread stop %d", n)
			} else if count > 16 {
				return nil, fmt.Errorf("could not stop process %d", n)
			}
		}
	}
}

func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}

func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}

func (dbp *nativeProcess) exitGuard(err error) error {
	if err != ErrContinueThread {
		return err

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Retry the halt; transient syscall states often clear on a second attempt.
  2. Inspect what the process is blocked on (sample <pid> or spindump) — uninterruptible syscalls need the syscall to return.
  3. Kill and restart the debug session if the task is wedged.
  4. Reduce target thread count or avoid halting during heavy I/O.
  5. Update Delve/macOS; suspend heuristics differ across OS releases.

Example fix

// before
(dlv) halt   // while target blocked in unread socket read
// after
// let the blocking syscall finish or close the peer, then:
(dlv) halt
Defensive patterns

Strategy: retry

Validate before calling

// Sample the process to detect uninterruptible waits before halting:
out, err := exec.Command("sample", strconv.Itoa(pid), "1").Output()
_ = out
if err != nil {
    return fmt.Errorf("cannot sample pid %d; process may be wedged", pid)
}

Try / catch

ports, err := dbp.waitForStop(dbp)
if err != nil && strings.Contains(err.Error(), "could not stop process") {
    // threads refused to suspend; back off and retry, or kill/restart the session
}

Prevention

When it happens

Trigger: stop() → waitForStop on macOS loops past count > 16 while C.num_running_threads(dbp.os.task) keeps returning > 0: threads ignore suspension, e.g. blocked in unminterruptible kernel states or the task is wedged.

Common situations: Halting a process stuck in uninterruptible syscalls (I/O, network); debuggee with a huge number of threads that can't all suspend in time; kernel/driver issues on specific macOS versions.

Related errors


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