{"record":{"id":"2cb93a2aa6abf883","repo":"n8n-io/n8n","slug":"already-connected-to-a-browser","errorCode":null,"errorMessage":"Already connected to a browser","messagePattern":"Already connected to a browser","errorType":"exception","errorClass":"AlreadyConnectedError","httpStatus":null,"severity":"warning","filePath":"packages/@n8n/mcp-browser/src/connection.ts","lineNumber":95,"sourceCode":"\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.config = {\n\t\t\tdefaultBrowser: parsed.defaultBrowser,\n\t\t\tbrowsers,\n\t\t\tadapter: parsed.adapter,\n\t\t\tmode: parsed.mode,\n\t\t};\n\t}\n\n\t// -------------------------------------------------------------------------\n\t// Public API\n\t// -------------------------------------------------------------------------\n\n\tasync connect(overrideBrowser?: BrowserName): Promise<ConnectResult> {\n\t\tif (this.state) {\n\t\t\tthrow new AlreadyConnectedError();\n\t\t}\n\n\t\tconst browser = overrideBrowser ?? this.config.defaultBrowser;\n\t\tif (this.config.mode !== 'remote') {\n\t\t\tthis.requireBrowserAvailable(browser);\n\t\t}\n\n\t\tconst connectConfig: ConnectConfig = {\n\t\t\tbrowser,\n\t\t};\n\n\t\t// Reuse a pending adapter (relay kept alive from a prior timeout) if available.\n\t\tconst adapter = this.pendingAdapter ?? (await this.createAdapter());\n\t\tthis.pendingAdapter = null;\n\n\t\t// Listen for unexpected disconnections so we can invalidate state immediately\n\t\tadapter.onDisconnect = (reason) => {\n\t\t\tif (!this.state) return; // already disconnected","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/mcp-browser/src/connection.ts#L77-L113","documentation":"AlreadyConnectedError is thrown by BrowserConnection.connect when this.state is already set — a prior connect() succeeded and the connection is live. The connection enforces single-active-session semantics; calling connect twice without an intervening disconnect is a programming error.","triggerScenarios":"Invoking browser_connect when a connection already exists (isConnected === true). Common in agent loops that reconnect defensively without first checking state, or in retry logic that re-enters connect after a partial success.","commonSituations":"Agent retries connect after a slow-but-successful first attempt. Two concurrent tool calls both triggering a lazy connect. User-script calls connect in a loop without disconnect.","solutions":["Check connection.isConnected before calling connect, or call disconnect first.","Make connect idempotent in your wrapper: if (connection.isConnected) return;","In retry loops, ensure disconnect() runs before any reconnect attempt."],"exampleFix":"// before — double connect\nawait connection.connect();\nawait connection.connect(); // throws AlreadyConnectedError\n\n// after — guard with isConnected\nif (!connection.isConnected) { await connection.connect(); }","handlingStrategy":"validation","validationCode":"if (connection.isConnected) { /* already connected, skip */ return; }\nawait connection.connect();","typeGuard":"function isConnected(c: BrowserConnection): boolean {\n  return c.isConnected;\n}","tryCatchPattern":"try {\n  await connection.connect();\n} catch (e) {\n  if (e instanceof AlreadyConnectedError) { /* no-op, already live */ }\n  else throw e;\n}","preventionTips":["Guard every connect call with isConnected.","Make your agent's connect wrapper idempotent.","Always pair connect/disconnect; never assume a clean slate."],"tags":["already-connected","state","connect","validation"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}