Field guide

How to read socket.io messages in Chrome DevTools

Chrome shows you every socket.io frame your app exchanges. It just shows them as the wire carries them: 42["chat message",{…}], a bare 2 every 25 seconds, and Binary Message (214 bytes) where your payload used to be. Here's how to read that by hand — and where hand-reading stops working.

Last updated 31 August 2026 · protocol details from the socket.io and engine.io v4 specs

Finding the frames

Open DevTools, go to Network, click the WS filter, select the connection, then the Messages tab. Green arrows are frames you sent, red are frames you received, and each row carries a timestamp and a length.

Two things to know before you trust that list:

Two protocols, stacked

What confuses most people is that a socket.io frame is two protocols in a trench coat. Engine.IO handles the connection — handshake, heartbeat, transport upgrade — and socket.io rides inside its message packets, carrying your events.

So the leading digits are not part of your data. They're framing, and each layer contributes one.

Engine.IO packet types (the first digit)
DigitTypeWhat it means when you see it
0openThe handshake. Payload is JSON with sid, upgrades, pingInterval, pingTimeout, maxPayload.
1closeTransport is closing.
2pingHeartbeat. In protocol v4 the server sends these, on the pingInterval from the handshake.
3pongThe client's answer. If it doesn't arrive within pingTimeout, the server drops the connection.
4messageEverything of yours. The rest of the frame is a socket.io packet.
5upgradePolling has been upgraded to WebSocket.
6noopFiller, used to wake up a hanging poll.
Socket.IO packet types (the second digit, inside a 4)
DigitTypeWhat it means when you see it
0CONNECTNamespace connection established.
1DISCONNECTLeaving a namespace.
2EVENTThe one you care about: ["eventName", …args].
3ACKThe callback firing for an event you acknowledged.
4CONNECT_ERRORRejected — usually auth middleware.
5BINARY_EVENTAn event with binary attachments sent as separate frames.
6BINARY_ACKSame, for an ack.

Decoding a frame by hand

The full socket.io encoding is:

<packet type>[<# of binary attachments>-][<namespace>,][<ack id>][JSON payload]

Everything in brackets is optional, and the namespace only appears when it isn't the default /. Wrapped in engine.io's 4, that gives you the frames you're actually staring at:

// engine.io handshake — the server opening the conversation
0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000}

// socket.io CONNECT on the default namespace: engine.io 4 + socket.io 0
40

// CONNECT on /admin, with the session id assigned to that namespace
40/admin,{"sid":"oSO0OpakMV_3jnilAAAA"}

// an EVENT: engine.io 4 + socket.io 2, then the array
42["chat message",{"user":"ana","text":"hi"}]

// the same EVENT, on /admin, expecting ack 12
42/admin,12["chat message",{…}]

// the ACK coming back for id 12
4312[{"ok":true}]

// a BINARY_EVENT with one attachment; the bytes arrive as the NEXT frame
451-["upload",{"_placeholder":true,"num":0}]

// heartbeat: server ping, client pong. Not your app. Ignore these.
2
3

Read it left to right and it collapses quickly. 42 is “a message, and it's an event”. 4312 is “a message, an ack, for id 12”. The bare 2 arriving every 25 seconds like clockwork is the server checking you're alive.

The one-line version

Strip the leading 4. If the next digit is 2, the rest is ["event", payload] — that's your message. Anything else is plumbing.

Where hand-reading stops

The digits are easy. These four are the ones that actually cost you an afternoon:

1. The frames you already missed

DevTools captures from the moment it opens. If the bug happens during connection — a failed auth handshake, a CONNECT_ERROR, an event fired on page load — you have to reproduce it with DevTools already open, and hope the bug is reproducible on demand.

2. Binary frames

A binary payload shows as Binary Message (n bytes). If your stack sends MessagePack, CBOR, protobuf or a custom struct, that row tells you a message happened and nothing about what it said.

3. JSON that's been through a laundry cycle

Plenty of production stacks nest their real payload inside a string field, then base64 it, then compress it. A single frame can be JSON → string field → base64 → gzip → more JSON. DevTools shows you the outermost layer, correctly escaped, and unhelpfully.

// what you see
42["sync",{"d":"H4sIAAAAAAAAA6tWKkotLsjPS1WyUlAqSy0qzszPU9JRSkksSVSyqlbKTS0uTkxPVbIC…"}]

// what it says once you base64-decode and gunzip the field
{"cursor":"8172","rows":[{"id":41,"price":"1.85"},…]}

4. Frames from iframes

If the socket lives inside an embedded widget, you're now hunting through a different execution context in a list that doesn't tell you which frame each row came from. That one has company: see the seven reasons frames don't show up in DevTools at all.

The manual workarounds

Before reaching for a tool, these two get you surprisingly far:

Both give you the client's view after parsing. Neither shows you the raw wire, which is exactly what you need when the bug is that the client parsed something differently than you expected.

Or read them already decoded

Wirepeek is a free Chrome DevTools panel built for this: it hooks WebSocket.prototype before socket.io grabs the native reference, so it captures from the handshake onward and survives reloads. socket.io and engine.io framing is resolved into event names and JSON, binary frames go through MessagePack, CBOR and Thrift decoders, and nested base64 or gzip fields get unwrapped in place. Read-only, no account, and nothing leaves your machine.

Add to Chrome — it's free

Quick reference

Frames you'll see most
FrameReading
0{…}engine.io handshake, carries sid and the heartbeat intervals
40socket.io connected, default namespace
42[…]your event, as ["name", …args]
42/ns,[…]your event, on namespace /ns
43<id>[…]ack for the event with that id
44{…}connect error — read the payload, it's usually the auth message
2 / 3heartbeat, server ping and client pong
41namespace disconnect

Protocol details here follow the socket.io protocol spec and the engine.io protocol spec (v4, the default since socket.io v3).