n8n-io/n8n · critical · ConnectionLostError
Browser connection lost: The connection to the browser exten
Error message
Browser connection lost: The connection to the browser extension was lost
What it means
ConnectionLostError('network_error') is thrown at the top of ExtensionConnection.send when the WebSocket readyState is not OPEN. Before queueing any request to the extension, send() checks the socket; a non-OPEN state (CONNECTING, CLOSING, CLOSED) means the channel is unusable. This is the pre-flight guard before any bytes are written.
Source
Thrown at packages/@n8n/mcp-browser/src/cdp-relay.ts:953
});
this.ws.on('error', (error) => {
log.debug('ExtensionConnection WebSocket error:', error);
this.handleClose();
});
this.ws.on('pong', () => {
this.lastPongAt = Date.now();
});
this.startHeartbeat();
}
async send<M extends keyof ExtensionCommands>(
method: M,
params: ExtensionCommands[M]['params'],
timeoutMs = 30_000,
): Promise<unknown> {
if (this.ws.readyState !== WebSocket.OPEN) {
throw new ConnectionLostError('network_error');
}
const id = ++this.lastId;
const payload = JSON.stringify({ id, method, params });
log.debug('→ EXT:', method, 'id=' + String(id));
try {
this.ws.send(payload);
} catch {
throw new ConnectionLostError('network_error');
}
return await new Promise((resolve, reject) => {
const timer = setTimeout(() => {
this.callbacks.delete(id);
log.error(
'→ EXT TIMEOUT:',
method,
'id=' + String(id),View on GitHub (pinned to 5ac6606e81)
Solutions
- Wait for the connection open event before issuing commands, or reconnect via browser_connect.
- Track connection liveness and queue/retry commands only while OPEN.
- Call browser_disconnect then browser_connect to obtain a fresh ExtensionConnection.
Example fix
// before — send before socket open
extConn.send('createTab', { url }); // readyState=CLOSED -> throws
// after — reconnect to get a fresh OPEN socket
await connection.disconnect(); await connection.connect();
await extConn.send('createTab', { url }); Defensive patterns
Strategy: validation
Validate before calling
if (extConn.ws.readyState !== WebSocket.OPEN) {
await connection.disconnect();
await connection.connect();
} Type guard
function socketOpen(ws: WebSocket): boolean {
return ws.readyState === WebSocket.OPEN;
} Try / catch
try {
return await extConn.send(method, params);
} catch (e) {
if (e instanceof ConnectionLostError && e.reason === 'network_error') {
await connection.disconnect();
await connection.connect();
return await extConn.send(method, params);
}
throw e;
} Prevention
- Wait for the WebSocket open event before issuing the first command.
- Track readyState transitions and reconnect before the next send.
- Do not reuse an ExtensionConnection after its socket closed.
When it happens
Trigger: Issuing any extension command (attachTab, createTab, forwardCDPCommand, etc.) when the WebSocket has transitioned out of OPEN — after handleClose fired, after close() was called, or while the socket is still handshaking.
Common situations: Command issued immediately after construction before the WS finishes opening. Command issued after the OS reported the TCP connection dead. Race between the disconnect event and an in-flight caller.
Related errors
- Browser connection lost: The browser extension disconnected
- Failed to fetch provider catalog: ${response.statusText}
- Supabase upsert failed: ${error.message}
- Supabase query failed: ${error.message}
- Supabase delete failed: ${error.message}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/1ccfd4f3f1c2022a.
Report an issue: GitHub.