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
- Use the stock delve version — this invariant should never fire in unmodified code
- If writing new gdbserial code, append ';thread:<id>' to the command instead of calling selectThread when conn.threadSuffixSupported
- 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
- Respect the threadSuffixSupported capability flag
- Append ';thread:<id>' instead of H packets when supported
- Never call selectThread with kind 'g'/'b' on suffixed stubs
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
- gdb protocol error: command doesn't start with '$'
- not implemented
- threadUpdater: Add after Finish
- unsupported operating system attempting to find Goroutine on
- must specify at least one argument for qRRCmd
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/463ca84589cbdc67.
Report an issue: GitHub.