{"record":{"id":"2ed05c5113d1b8ce","repo":"go-delve/delve","slug":"gdb-protocol-error-command-doesn-t-start-with","errorCode":null,"errorMessage":"gdb protocol error: command doesn't start with '$'","messagePattern":"gdb protocol error: command doesn't start with '\\$'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proc/gdbserial/gdbserver_conn.go","lineNumber":1333,"sourceCode":"\treturn mri, nil\n}\n\n// exec executes a message to the stub and reads a response.\n// The details of the wire protocol are described here:\n//\n//\thttps://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview\nfunc (conn *gdbConn) exec(cmd []byte, context string) ([]byte, error) {\n\tif err := conn.send(cmd); err != nil {\n\t\treturn nil, err\n\t}\n\treturn conn.recv(cmd, context, false)\n}\n\nconst hexdigit = \"0123456789abcdef\"\n\nfunc (conn *gdbConn) send(cmd []byte) error {\n\tif len(cmd) == 0 || cmd[0] != '$' {\n\t\tpanic(\"gdb protocol error: command doesn't start with '$'\")\n\t}\n\n\t// append checksum to packet\n\tcmd = append(cmd, '#')\n\tsum := checksum(cmd)\n\tcmd = append(cmd, hexdigit[sum>>4], hexdigit[sum&0xf])\n\n\tattempt := 0\n\tfor {\n\t\tif logflags.GdbWire() {\n\t\t\tif len(cmd) > gdbWireMaxLen {\n\t\t\t\tconn.log.Debugf(\"<- %s...\", string(cmd[:gdbWireMaxLen]))\n\t\t\t} else {\n\t\t\t\tconn.log.Debugf(\"<- %s\", string(cmd))\n\t\t\t}\n\t\t}\n\t\t_, err := conn.conn.Write(cmd)\n\t\tif err != nil {","sourceCodeStart":1315,"sourceCodeEnd":1351,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/proc/gdbserial/gdbserver_conn.go#L1315-L1351","documentation":"send() is the low-level gdbserial packet transmitter; every GDB remote command must begin with '$' and end with '#' plus a two-hex-digit checksum. The panic guards against corrupt or empty command buffers reaching the wire.","triggerScenarios":"Any code path that builds a command into conn.outbuf incorrectly (e.g. calling send with an empty buffer, or a buffer whose first byte was overwritten/reset improperly) then calls conn.send(cmd).","commonSituations":"Forks or patches of delve that construct raw packets; a failed/interleaved outbuf.Reset() leaving an empty buffer; hand-written protocol experiments against the gdbserial package.","solutions":["Inspect the caller building the command — ensure it starts with '$' and is non-empty","Verify outbuf is populated (Fprintf to outbuf) before exec/send","Use unmodified delve; if triggered during normal debugging, capture the packet log and report upstream"],"exampleFix":"// before\nconn.send(rawBytes) // rawBytes may be empty\n// after\nif len(rawBytes) == 0 || rawBytes[0] != '$' {\n    return errors.New(\"malformed gdb packet\")\n}\nconn.send(rawBytes)","handlingStrategy":"validation","validationCode":"if len(cmd) == 0 || cmd[0] != '$' {\n    return errors.New(\"malformed gdb packet: must start with $\")\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil { err = fmt.Errorf(\"send: %v\", r) }\n}()","preventionTips":["Always build packets via the provided helpers (exec/Fprintf into outbuf)","Never cache/reuse partially written buffers without Reset+rebuild","Log outgoing packets in tests to catch malformed construction early"],"tags":["gdbserial","gdb-remote-protocol","panic","protocol-error"],"backgroundTag":"gdb-protocol-invariant-violation","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}