go-delve/delve · warning

thread blocked

Error message

thread blocked

What it means

errThreadBlocked is an internal sentinel error returned by waitForvContStop. When the vCont continue/step is issued and a stop packet indicates the thread could not run because it is blocked (e.g. blocked in a syscall or waiting on another thread's stop), the connection uses this error to signal that state to its caller, which typically retries or resolves the thread state.

Source

Thrown at pkg/proc/gdbserial/gdbserver_conn.go:692

			return nil
		case childSignal: // stop on debugserver but SIGCHLD on lldb-server/linux
			if conn.isDebugserver {
				return nil
			}
		case debugServerTargetExcBadAccess, debugServerTargetExcBadInstruction, debugServerTargetExcArithmetic, debugServerTargetExcEmulation, debugServerTargetExcSoftware, debugServerTargetExcBreakpoint:
			if ignoreFaultSignal {
				return nil
			}
			return machTargetExcToError(sig)
		default:
			// delay propagation of any other signal to until after the stepping is done
			th.sig = sig
			sig = 0
		}
	}
}

var errThreadBlocked = errors.New("thread blocked")

func (conn *gdbConn) waitForvContStop(context, threadID string, tu *threadUpdater) (stopPacket, error) {
	count := 0
	failed := false
	for {
		conn.conn.SetReadDeadline(time.Now().Add(heartbeatInterval))
		resp, err := conn.recv(nil, context, false)
		conn.conn.SetReadDeadline(time.Time{})
		if neterr, isneterr := err.(net.Error); isneterr && neterr.Timeout() {
			// Debugserver sometimes forgets to inform us that inferior stopped,
			// sending this status request after a timeout helps us get unstuck.
			// Debugserver will not respond to this request unless inferior is
			// already stopped.
			if conn.isDebugserver {
				conn.send([]byte("$?"))
			}
			if count > 1 && context == "singlestep" {
				failed = true

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Retry the continue/step — Delve handles this internally; if it surfaces, simply continue again
  2. Resume other threads first or use process-wide continue instead of stepping a single thread
  3. Check whether the target thread is stuck in a syscall and let the program progress to a runnable state
  4. If it recurs persistently, restart the session; some stub versions have blocked-thread reporting bugs
Defensive patterns

Strategy: retry

Try / catch

if err := sess.Step(); err != nil {
    if strings.Contains(err.Error(), "thread blocked") {
        // thread is in a blocking syscall; wait and retry or continue all threads
        time.Sleep(100 * time.Millisecond)
        err = sess.Continue()
    }
}

Prevention

When it happens

Trigger: Calling ContinueOnce/step on the gdbserial backend when waitForvContStop reads a stop reply showing the thread is blocked rather than truly stopped at a signal; common with all-stop mode where one thread cannot resume until others stop.

Common situations: Stepping a thread that is parked in a blocking syscall (read/write/futex); debugging multithreaded programs via lldb-server/debugserver where thread scheduling interlocks; single-thread resume requests during process-wide stops.

Related errors


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