Recommended design for handling WebSocket disconnects in realtime sessions.
Reconnect decision matrix
| Close code | Meaning | Action | |---:|---|---| | 1000 | Normal close | Do not reconnect | | 4408 | Idle timeout | Reconnect immediately | | 1008 | Session duration limit | Reconnect immediately (new session) | | 1009 | Frame too large | Fix frame size, then reconnect | | 1011 | Client socket error | Reconnect with backoff | | 1013 | Upstream unavailable | Exponential backoff |
Exponential backoff
function reconnectWithBackoff(url, maxRetries = 5) {
let attempt = 0;
let lastLanguage = 'zh';
function connect() {
const ws = new WebSocket(url);
ws.onopen = () => {
console.log('Connected');
attempt = 0; // reset backoff on success
// Re-send session configuration
ws.send(JSON.stringify({ type: 'session.update', language: lastLanguage }));
};
ws.onclose = (event) => {
if (event.code === 1000) return; // normal close
if (attempt < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
attempt++;
console.log(Reconnecting in ${delay}ms (attempt ${attempt}));
setTimeout(connect, delay);
}
};
ws.onmessage = (event) => {
// Handle events as normal
};
}
connect();
}
Session continuation
After reconnecting:
session_id — sessions do not resumesession.update to restore language, VAD, and other settingsKeep-alive strategies
To avoid idle timeout (4408):
// Send silent frames to keep connection alive
function keepAlive(ws) {
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
const silence = new ArrayBuffer(3200); // 100ms of silence
ws.send(silence);
}
}, 5000); // every 5 seconds
}
Session token refresh
Browser connections: session tokens expire in 60 seconds. If reconnection happens after token expiry:
async function connectWithFreshToken() {
const { token } = await fetch('/session-token', { method: 'POST' }).then(r => r.json());
return new WebSocket(
wss://audio.lansonai.com/v1/audio/transcriptions/stream?access_token=${token}
);
}