microsoft/typescript-go · error · ErrInvalidRequest

%w: expected binary data (0xc4-0xc6), received: 0x%02x

Error message

%w: expected binary data (0xc4-0xc6), received: 0x%02x

What it means

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.

Source

Thrown at internal/api/protocol_msgpack.go:172

		var size8 uint8
		if err = binary.Read(p.r, binary.BigEndian, &size8); err != nil {
			return nil, err
		}
		size = uint(size8)
	case msgpackBin16:
		var size16 uint16
		if err = binary.Read(p.r, binary.BigEndian, &size16); err != nil {
			return nil, err
		}
		size = uint(size16)
	case msgpackBin32:
		var size32 uint32
		if err = binary.Read(p.r, binary.BigEndian, &size32); err != nil {
			return nil, err
		}
		size = uint(size32)
	default:
		return nil, fmt.Errorf("%w: expected binary data (0xc4-0xc6), received: 0x%02x", ErrInvalidRequest, t)
	}

	payload := make([]byte, size)
	if _, err := io.ReadFull(p.r, payload); err != nil {
		return nil, err
	}
	return payload, nil
}

// WriteRequest implements Protocol.
func (p *MessagePackProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error {
	// For msgpack protocol, requests from server are "Call" type
	payload, err := json.Marshal(params)
	if err != nil {
		return err
	}
	return p.writeTuple(MessageTypeCall, method, payload)
}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. 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
  2. Verify the full frame layout: a fixed 3-element array 0x93 [messageType (fixint/uint8), method bin, payload bin]
  3. 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
  4. If you meant to speak JSON-RPC, start the server with StdioServerOptions.Async = true (and JSONRPCProtocol) instead of msgpack

Example fix

// before (string encodes as msgpack str 0xa0-0xd9 -> error)
const frame = [msgType, "getSymbolAtPosition", JSON.stringify(params)];
write(msgpack.encode(frame));

// after (Uint8Array encodes as bin 0xc4-0xc6)
const frame = [msgType,
  new TextEncoder().encode("getSymbolAtPosition"),
  new TextEncoder().encode(JSON.stringify(params))];
write(msgpack.encode(frame)); // 0x93 <type> 0xc4.. 0xc4..
Defensive patterns

Strategy: validation

Validate before calling

// Before writing a frame, assert the method/payload fields will encode as msgpack bin.
function assertBinField(v: unknown, name: string): asserts v is Uint8Array {
  if (!(v instanceof Uint8Array)) {
    throw new TypeError(`${name} must be Uint8Array/Buffer so msgpack emits bin (0xc4-0xc6), got ${typeof v}`);
  }
}
assertBinField(methodBytes, "method");
assertBinField(payloadBytes, "payload");

Type guard

const isBinEncodable = (v: unknown): v is Uint8Array => v instanceof Uint8Array || (typeof Buffer !== "undefined" && v instanceof Buffer);

Try / catch

try {
  await conn.write(frame);
} catch (e) {
  if (String(e).includes("api: invalid request") && String(e).includes("0xc4-0xc6")) {
    // Stream framing is wrong/desynced; do NOT retry on this connection.
    await conn.close();
    throw new Error("msgpack framing error: encode method/payload as bin types", { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/6cb6725d9f459d17. Report an issue: GitHub.