{"record":{"id":"463ca84589cbdc67","repo":"go-delve/delve","slug":"selectthread-when-thread-suffix-is-supported","errorCode":null,"errorMessage":"selectThread when thread suffix is supported","messagePattern":"selectThread when thread suffix is supported","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/gdbserial/gdbserver_conn.go","lineNumber":997,"sourceCode":"\t\tthreads = append(threads, string(tidbuf))\n\t\tif comma < 0 {\n\t\t\tbreak\n\t\t}\n\t\tresp = resp[comma+1:]\n\t}\n\n\tif conn.multiprocess && pid > 0 {\n\t\tconn.pid = pid\n\t}\n\treturn threads, nil\n}\n\nfunc (conn *gdbConn) selectThread(kind byte, threadID string, context string) error {\n\tif conn.threadSuffixSupported && kind != 'c' {\n\t\t// kind == 'c' is still allowed because 'rr' is weird about it's support\n\t\t// for thread suffixes and the specification for it doesn't really say how\n\t\t// it should be used with packets 'bc' and 'bs'.\n\t\tpanic(\"selectThread when thread suffix is supported\")\n\t}\n\tconn.outbuf.Reset()\n\tfmt.Fprintf(&conn.outbuf, \"$H%c%s\", kind, threadID)\n\t_, err := conn.exec(conn.outbuf.Bytes(), context)\n\treturn err\n}\n\nfunc (conn *gdbConn) appendThreadSelector(threadID string) {\n\tif !conn.threadSuffixSupported {\n\t\treturn\n\t}\n\tfmt.Fprintf(&conn.outbuf, \";thread:%s;\", threadID)\n}\n\nfunc (conn *gdbConn) readMemory(data []byte, addr uint64) error {\n\tif conn.xcmdok && len(data) > conn.packetSize {\n\t\treturn conn.readMemoryBinary(data, addr)\n\t}","sourceCodeStart":979,"sourceCodeEnd":1015,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/gdbserial/gdbserver_conn.go#L979-L1015","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconn.selectThread('g', threadID, \"\")\n// after\nif conn.threadSuffixSupported {\n    cmd += \";thread:\" + threadID // thread-suffixed packet\n} else {\n    conn.selectThread('g', threadID, \"\")\n}","handlingStrategy":"validation","validationCode":"if conn.threadSuffixSupported && kind != 'c' {\n    // use thread suffix on the command instead of H packet\n    return errors.New(\"use thread-suffixed packet\")\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil { err = fmt.Errorf(\"selectThread: %v\", r) }\n}()","preventionTips":["Respect the threadSuffixSupported capability flag","Append ';thread:<id>' instead of H packets when supported","Never call selectThread with kind 'g'/'b' on suffixed stubs"],"tags":["gdbserial","gdb-remote-protocol","panic"],"backgroundTag":"gdb-protocol-invariant-violation","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}