This episode covers connection failures: the types of errors, proper handling patterns, reconnection strategies with exponential backoff, Socket.IO's built-in configuration, and recovery of state and lost messages.

Networks are never perfect. A dropped WiFi connection, a server restart, a failing VPN, or a phone switching networks — all of these break WebSocket connections. A good application does not give up when the connection drops; it retries intelligently and recovers the state.
Episode 12 covers error handling: recognizing and dealing with the various failures, and reconnection strategies: retrying with exponential backoff, limiting attempts, and ensuring no data is lost.
Connection failures come from many sources with different characteristics. The common failures you will encounter:
Each error type needs a different response: retry for network failure, wait for server unavailability, and inform the user about auth failures.
The browser provides dedicated events for each connection phase.
const ws = new WebSocket("wss://api.example.com/ws");
ws.onerror = (event) => {
console.error("error WebSocket:", event.message);
};
ws.onclose = (event) => {
console.log("koneksi ditutup:", event.code, event.reason);
if (event.code !== 1000) {
cobaKoneksiKembali();
}
};The onerror event tells you something went wrong, and onclose carries the close code. Code 1000 means a normal close; other codes such as 1006 (abnormal) indicate a dropped connection that needs retrying.
The server must also handle errors calmly, without crashing.
wss.on("connection", (ws) => {
ws.on("error", (err) => {
console.error("error koneksi:", err.message);
});
ws.on("message", (data) => {
try {
prosesPesan(data);
} catch (err) {
ws.send(JSON.stringify({ error: err.message }));
}
});
});Every connection has its own error handler. Message processing is wrapped in try-catch so one corrupted message does not stop the entire process.
Retrying immediately over and over will burden a server that is already struggling. The solution is to increase the delay exponentially.
let percobaan = 0;
const MAKS_PERCOBAAN = 5;
function hubungkan() {
const jeda = Math.min(1000 * 2 ** percobaan, 30000);
setTimeout(() => {
const ws = new WebSocket("wss://api.example.com/ws");
ws.onopen = () => {
percobaan = 0;
};
ws.onclose = () => {
percobaan += 1;
if (percobaan <= MAKS_PEROBAAN) {
hubungkan();
}
};
}, jeda);
}Math.min(1000 * 2 ** percobaan, 30000) produces delays of 1, 2, 4 seconds up to a 30-second cap. Every successful connection resets the counter, so a server restart does not crush the client.
An unbounded reconnect can last forever. Limit the number of attempts and show an honest message when giving up: after five failed attempts, present the option to reload the page. Users are more patient when told what is happening than when waiting in silence.
Socket.IO has automatic reconnection configurable with several options.
const socket = io("https://api.example.com", {
reconnection: true,
reconnectionAttempts: 10,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
});
socket.on("reconnect_attempt", (attempt) => {
console.log("percobaan ke:", attempt);
});
socket.on("reconnect_failed", () => {
console.log("semua percobaan gagal");
});reconnection: true enables automatic reconnect, reconnectionDelayMax caps the maximum delay. The reconnect_attempt and reconnect_failed events give you visibility into the process.
For full control, turn off the built-in reconnection and manage it yourself.
const socket = io("https://api.example.com", {
reconnection: false,
});reconnection: false disables the automation, and socket.connect() triggers a manual reconnect. This mode is useful when combining reconnection with specific business logic.
When the connection recovers, the client must resynchronize its state with the server.
socket.on("connect", () => {
socket.emit("sync:request", { dariId: idPesanTerakhir });
socket.emit("presence:set", { status: "online" });
socket.emit("room:rejoin", daftarRoom);
});idPesanTerakhir lets the server resend missed messages. Rejoining rooms and resetting presence ensures the client returns to the correct position after a reconnect.
Episode 12 turned network failures from a disaster into a handled event: errors are recognized, reconnect happens with ever-longer delays, and state is restored after the connection returns.
Key takeaways:
In the next episode we cover rate limiting & throttling: token bucket and sliding window algorithms, per-user limits, and how to handle backpressure on the client and server.