{"record":{"id":"6cb6725d9f459d17","repo":"microsoft/typescript-go","slug":"w-expected-binary-data-0xc4-0xc6-received-0x","errorCode":null,"errorMessage":"%w: expected binary data (0xc4-0xc6), received: 0x%02x","messagePattern":"%w: expected binary data \\(0xc4-0xc6\\), received: 0x%02x","errorType":"validation","errorClass":"ErrInvalidRequest","httpStatus":null,"severity":"error","filePath":"internal/api/protocol_msgpack.go","lineNumber":172,"sourceCode":"\t\tvar size8 uint8\n\t\tif err = binary.Read(p.r, binary.BigEndian, &size8); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsize = uint(size8)\n\tcase msgpackBin16:\n\t\tvar size16 uint16\n\t\tif err = binary.Read(p.r, binary.BigEndian, &size16); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsize = uint(size16)\n\tcase msgpackBin32:\n\t\tvar size32 uint32\n\t\tif err = binary.Read(p.r, binary.BigEndian, &size32); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsize = uint(size32)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%w: expected binary data (0xc4-0xc6), received: 0x%02x\", ErrInvalidRequest, t)\n\t}\n\n\tpayload := make([]byte, size)\n\tif _, err := io.ReadFull(p.r, payload); err != nil {\n\t\treturn nil, err\n\t}\n\treturn payload, nil\n}\n\n// WriteRequest implements Protocol.\nfunc (p *MessagePackProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error {\n\t// For msgpack protocol, requests from server are \"Call\" type\n\tpayload, err := json.Marshal(params)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn p.writeTuple(MessageTypeCall, method, payload)\n}","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/protocol_msgpack.go#L154-L190","documentation":"The MessagePack protocol reader (MessagePackProtocol.readBin) expects the method and payload fields of every request tuple to be encoded as msgpack binary types bin8 (0xc4), bin16 (0xc5) or bin32 (0xc6), and it received a different type byte where binary data should start. This is a wire-framing violation: the server treats the incoming byte stream as desynchronized or incorrectly encoded and rejects the whole request wrapped in ErrInvalidRequest (\"api: invalid request\"). Note that most msgpack encoders serialize JS strings as str types (fixstr 0xa0-0xbf, str8 0xd9, str16 0xda), not bin, so naively encoding a string method name triggers exactly this error.","triggerScenarios":"Sending the method name or JSON payload as a msgpack string (str family) instead of bin; hand-rolling the frame and writing fixstr/array/nil bytes where the bin marker belongs; any earlier malformed frame that shifts the read offset so the next byte read as a bin marker is actually part of a previous value; using a different msgpack flavor (e.g. JSON-RPC text) on a connection the server opened with NewMessagePackProtocol (the sync, non-Async mode).","commonSituations":"A TypeScript client using @msgpack/msgpack, which encodes strings as str by default; porting a client from the JSON-RPC (Async: true) mode to the default msgpack mode and still sending text frames; a truncated write or partial flush that desyncs the stream; version skew between client encoder and server protocol expectations after upgrading typescript-go.","solutions":["Encode both the method and the payload as msgpack bin: pass Uint8Array/Buffer values (e.g. Buffer.from(method, 'utf8') and Buffer.from(JSON.stringify(params))) so the encoder emits 0xc4-0xc6","Verify the full frame layout: a fixed 3-element array 0x93 [messageType (fixint/uint8), method bin, payload bin]","If a bad frame was already sent, tear down and re-establish the connection - the byte stream may be desynchronized and no retry on the same connection will recover","If you meant to speak JSON-RPC, start the server with StdioServerOptions.Async = true (and JSONRPCProtocol) instead of msgpack"],"exampleFix":"// before (string encodes as msgpack str 0xa0-0xd9 -> error)\nconst frame = [msgType, \"getSymbolAtPosition\", JSON.stringify(params)];\nwrite(msgpack.encode(frame));\n\n// after (Uint8Array encodes as bin 0xc4-0xc6)\nconst frame = [msgType,\n  new TextEncoder().encode(\"getSymbolAtPosition\"),\n  new TextEncoder().encode(JSON.stringify(params))];\nwrite(msgpack.encode(frame)); // 0x93 <type> 0xc4.. 0xc4..","handlingStrategy":"validation","validationCode":"// Before writing a frame, assert the method/payload fields will encode as msgpack bin.\nfunction assertBinField(v: unknown, name: string): asserts v is Uint8Array {\n  if (!(v instanceof Uint8Array)) {\n    throw new TypeError(`${name} must be Uint8Array/Buffer so msgpack emits bin (0xc4-0xc6), got ${typeof v}`);\n  }\n}\nassertBinField(methodBytes, \"method\");\nassertBinField(payloadBytes, \"payload\");","typeGuard":"const isBinEncodable = (v: unknown): v is Uint8Array => v instanceof Uint8Array || (typeof Buffer !== \"undefined\" && v instanceof Buffer);","tryCatchPattern":"try {\n  await conn.write(frame);\n} catch (e) {\n  if (String(e).includes(\"api: invalid request\") && String(e).includes(\"0xc4-0xc6\")) {\n    // Stream framing is wrong/desynced; do NOT retry on this connection.\n    await conn.close();\n    throw new Error(\"msgpack framing error: encode method/payload as bin types\", { cause: e });\n  }\n  throw e;\n}","preventionTips":["Always wrap method and payload in TextEncoder().encode()/Buffer.from so the encoder emits bin, never str","Use one well-tested frame-builder function for all requests instead of ad-hoc encodings","Add a round-trip unit test: encode a request, decode it, assert the type bytes are 0x93/0xc4-0xc6","Never mix protocols on one connection; msgpack mode is the default, JSON-RPC requires Async: true"],"tags":["msgpack","protocol","serialization","binary","framing","client-error"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}