go-delve/delve · error

stub exited while attempting to connect: %v

Error message

stub exited while attempting to connect: %v

What it means

gdbProcess.Dial connects to an already-running gdbserial stub. While waiting for the stub to become connectable it polls the connection in a loop; if at any point the wait channel reports the stub process has terminated, Dial aborts with 'stub exited while attempting to connect' wrapping the exit status.

Source

Thrown at pkg/proc/gdbserial/gdbserver.go:320

			return nil, err
		}
		return nil, fmt.Errorf("stub exited while waiting for connection: %v", status)
	}
}

// Dial attempts to connect to the stub.
func (p *gdbProcess) Dial(addr string, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
	for {
		conn, err := net.Dial("tcp", addr)
		if err == nil {
			return p.Connect(conn, path, cmdline, pid, debugInfoDirs, stopReason)
		}
		select {
		case status := <-p.waitChan:
			if err := checkRosettaExpensive(); err != nil {
				return nil, err
			}
			return nil, fmt.Errorf("stub exited while attempting to connect: %v", status)
		default:
		}
		time.Sleep(time.Second)
	}
}

// Connect connects to a stub and performs a handshake.
//
// Path and pid are, respectively, the path to the executable of the target
// program and the PID of the target process, both are optional, however
// some stubs do not provide ways to determine path and pid automatically
// and Connect will be unable to function without knowing them.
func (p *gdbProcess) Connect(conn net.Conn, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
	p.conn.conn = conn
	p.conn.pid = pid
	err := p.conn.handshake(p.regnames)
	if err != nil {
		conn.Close()

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Check the wrapped exit status and inspect the stub's output/logs to see why it terminated before the connection completed.
  2. Verify the stub process is still alive (ps) at the address you're dialing, and restart it if needed, then connect promptly.
  3. Ensure only one client connects; a stub typically serves a single connection and exits after another client attaches.
  4. Use a stable, supported stub version and environment (matching Delve and OS/arch), especially in containers/CI.
Defensive patterns

Strategy: validation

Validate before calling

// before dialing, confirm the stub is listening and alive
conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil {
    return fmt.Errorf("no stub listening at %s: %w", addr, err)
}
conn.Close()

Try / catch

tg, err := dbg.Dial(addr, ...)
if err != nil && strings.Contains(err.Error(), "stub exited while attempting to connect") {
    return fmt.Errorf("target stub terminated before connection: %w; restart the headless session", err)
}

Prevention

When it happens

Trigger: Calling debugger.Dial()/connect to a headless stub whose process died while Delve was retrying the connection loop — the stub crashed after printing its port, was killed externally, or exited because another client already connected.

Common situations: Connecting to a `dlv --headless`/lldb-server process that crashed or was terminated before the client attached; two clients racing to connect to the same stub; stale address/port reused after the stub exited; container/CI environments reaping the stub process.

Related errors


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