{"record":{"id":"acb8ab88dfb7ffb6","repo":"chenhg5/cc-connect","slug":"yuanbao-empty-conn-msg","errorCode":null,"errorMessage":"yuanbao: empty conn msg","messagePattern":"yuanbao: empty conn msg","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/yuanbao/proto.go","lineNumber":264,"sourceCode":"type connHead struct {\n\tcmdType int\n\tcmd     string\n\tseqNo   int\n\tmsgID   string\n\tmodule  string\n\tneedAck bool\n\tstatus  int\n}\n\ntype connMsg struct {\n\thead  connHead\n\tseqNo int\n\tdata  []byte\n}\n\nfunc decodeConnMsg(raw []byte) (*connMsg, error) {\n\tif len(raw) == 0 {\n\t\treturn nil, fmt.Errorf(\"yuanbao: empty conn msg\")\n\t}\n\tfields, err := parseFields(raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\theadBytes := getBytes(fields, 1)\n\tpayload := getBytes(fields, 2)\n\tvar head connHead\n\tif len(headBytes) > 0 {\n\t\thf, err := parseFields(headBytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"yuanbao: parse head: %w\", err)\n\t\t}\n\t\thead = connHead{\n\t\t\tcmdType: int(getVarint(hf, 1)),\n\t\t\tcmd:     getString(hf, 2),\n\t\t\tseqNo:   int(getVarint(hf, 3)),\n\t\t\tmsgID:   getString(hf, 4),","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/yuanbao/proto.go#L246-L282","documentation":"Thrown by decodeConnMsg when handed a zero-length raw buffer: there are no fields to parse at all, so no connMsg can be produced. It's a fast-fail guard before parseFields is invoked, distinguishing 'empty payload' from 'malformed payload'.","triggerScenarios":"The transport delivered a frame whose body is empty (heartbeat/keepalive variant not handled upstream), or a caller slices a zero-length region (e.g. getBytes returned nil for a missing field 2 and that nil is re-decoded). Reached from authenticate, handleFrame, and tests.","commonSituations":"Handling a ping/pong or ack frame with no payload through the connMsg path; server sending an empty DATA frame; a sub-message field absent so getBytes returns nil which is then decoded.","solutions":["Check len(raw) before calling decodeConnMsg and skip/dispatch empty frames to the appropriate handler (ping/ack).","Treat an empty payload in a message-expecting frame as a protocol error and log it with the frame command type from the header.","If the empty buffer comes from getBytes(fields, N) returning nil for an absent field, check field presence before decoding the sub-message.","Verify the server isn't sending empty bodies due to an API change; capture a frame dump to confirm."],"exampleFix":"// before\nraw := frame.Payload\ncm, err := decodeConnMsg(raw)\n// after\nif len(frame.Payload) == 0 {\n    switch frame.Cmd { // dispatch empties to heartbeat handling\n    case cmdPing:\n        handlePing(); return\n    default:\n        slog.Warn(\"yuanbao: empty conn msg body\", \"cmd\", frame.Cmd)\n        return\n    }\n}\ncm, err := decodeConnMsg(frame.Payload)","handlingStrategy":"validation","validationCode":"func shouldDecodeConnMsg(frame Frame) bool {\n    return len(frame.Payload) > 0\n}","typeGuard":null,"tryCatchPattern":"cm, err := decodeConnMsg(raw)\nif err != nil {\n    if strings.Contains(err.Error(), \"empty conn msg\") {\n        return nil // benign: heartbeat/empty frame\n    }\n    return err\n}","preventionTips":["Dispatch empty frames (ping/ack) before the connMsg decode path.","Check field presence before re-decoding sub-messages from getBytes results.","Log the frame command alongside empty-payload warnings.","Add a test asserting heartbeats never reach decodeConnMsg."],"tags":["protobuf","empty-payload","yuanbao"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}