← Dev log
EduQate·distributed & realtime·2026-07-20

A WebSocket client joining before the server allowed it

Socket.io handleConnection is async and sets client.data.token only after awaiting, so a fast client reached the join handler before auth finished and found no token.

It could not have worked

A race, an ordering, one column holding two things. The shape ruled it out before the code ran.

Found during a production-depth review, not from a report. RealtimeGateway.handleConnection is async — it resolves the token and only then sets client.data.token. But that firing and the client's next action (joining a conversation room, which the frontend does immediately) aren't ordered relative to each other. A fast client could reach handleJoin before the async work finished, find no token, and get silently disconnected through no fault of its own.

A manual click-test would almost never catch this, since the resolution usually finishes well within a round-trip. It would surface as an intermittent "sometimes messaging just doesn't connect," which is the worst kind of ticket.

handleJoin now re-derives the token from the handshake if the cached value isn't there. Verified with a standalone socket.io-client script hammering the join-on-connect sequence, 5/5 clean after previously reproducing the disconnect. An async connection handler that sets state other handlers depend on is a race by construction — worth asking "what if the next message arrives before this promise settles" every time.