go-delve/delve · error

selectThread when thread suffix is supported

Error message

selectThread when thread suffix is supported

What it means

selectThread sends the GDB remote 'H' packet to set the current thread. When the stub advertises thread-suffix support (';thread:<id>' appended to packets), the separate H packet must not be used for most kinds, so the code panics as an internal invariant check. Only kind 'c' is exempt because of rr's quirky behavior with bc/bs packets.

Source

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

		threads = append(threads, string(tidbuf))
		if comma < 0 {
			break
		}
		resp = resp[comma+1:]
	}

	if conn.multiprocess && pid > 0 {
		conn.pid = pid
	}
	return threads, nil
}

func (conn *gdbConn) selectThread(kind byte, threadID string, context string) error {
	if conn.threadSuffixSupported && kind != 'c' {
		// kind == 'c' is still allowed because 'rr' is weird about it's support
		// for thread suffixes and the specification for it doesn't really say how
		// it should be used with packets 'bc' and 'bs'.
		panic("selectThread when thread suffix is supported")
	}
	conn.outbuf.Reset()
	fmt.Fprintf(&conn.outbuf, "$H%c%s", kind, threadID)
	_, err := conn.exec(conn.outbuf.Bytes(), context)
	return err
}

func (conn *gdbConn) appendThreadSelector(threadID string) {
	if !conn.threadSuffixSupported {
		return
	}
	fmt.Fprintf(&conn.outbuf, ";thread:%s;", threadID)
}

func (conn *gdbConn) readMemory(data []byte, addr uint64) error {
	if conn.xcmdok && len(data) > conn.packetSize {
		return conn.readMemoryBinary(data, addr)
	}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Use the stock delve version — this invariant should never fire in unmodified code
  2. If writing new gdbserial code, append ';thread:<id>' to the command instead of calling selectThread when conn.threadSuffixSupported
  3. Reproduce with a debug log of the packet sequence and report it to go-delve/delve with the stub name/version

Example fix

// before
conn.selectThread('g', threadID, "")
// after
if conn.threadSuffixSupported {
    cmd += ";thread:" + threadID // thread-suffixed packet
} else {
    conn.selectThread('g', threadID, "")
}
Defensive patterns

Strategy: validation

Validate before calling

if conn.threadSuffixSupported && kind != 'c' {
    // use thread suffix on the command instead of H packet
    return errors.New("use thread-suffixed packet")
}

Try / catch

defer func() {
    if r := recover(); r != nil { err = fmt.Errorf("selectThread: %v", r) }
}()

Prevention

When it happens

Trigger: An internal code path calling conn.selectThread with kind 'g', 'b', etc. (any kind other than 'c') on a connection whose threadSuffixSupported flag is true.

Common situations: Modifying gdbserial packet-sending code and forgetting to append thread suffixes instead of using H packets; connecting to a stub (lldb debugserver, rr) that supports thread suffixes while using an older/patched Delve that still issues H packets.

Related errors


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