musistudio/claude-code-router · error · Error

Browser automation session tabId does not match the attached

Error message

Browser automation session tabId does not match the attached session.

What it means

Thrown when reusing a known sessionId whose stored attached session points at a different tabId than the ref in the current request. Sessions are pinned to one tab; the mismatch guard prevents a stale or hand-edited session ref from silently retargeting an established session to another tab.

Source

Thrown at packages/electron/src/main/browser-automation-mcp.ts:1226

  }

  private requireSession(args: Record<string, unknown>): AttachedSession {
    const session = this.resolveOptionalSession(args);
    if (!session) {
      throw new Error("A valid browser automation session is required.");
    }
    return session;
  }

  private resolveOptionalSession(args: Record<string, unknown>): AttachedSession | undefined {
    const ref = readSessionRef(args.session);
    if (!ref) {
      return undefined;
    }
    const existing = this.sessions.get(ref.sessionId);
    if (existing) {
      if (existing.ref.tabId !== ref.tabId) {
        throw new Error("Browser automation session tabId does not match the attached session.");
      }
      return existing;
    }
    const state = builtInBrowserService.getAutomationState();
    if (!state.tabs.some((tab) => tab.id === ref.tabId)) {
      throw new Error(`Browser automation session tab was not found: ${ref.tabId}`);
    }
    const restored: AttachedSession = {
      attachedAt: Date.now(),
      leaseId: randomUUID(),
      observeOnly: false,
      ref
    };
    this.sessions.set(ref.sessionId, restored);
    return restored;
  }

  private assertCanMutate(session?: AttachedSession): void {

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Keep the session object unchanged from what the attach call returned.
  2. To operate on a different tab, create/attach a new session for that tabId.
  3. After a tab closes (sessions for it are removed), re-attach fresh rather than mutating the old ref.

Example fix

// before
await call("browser_tool", { session: { sessionId, tabId: otherTabId } });

// after
const { session: sessionB } = await call("browser_attach", { tabId: otherTabId });
await call("browser_tool", { session: sessionB });
Defensive patterns

Strategy: validation

Validate before calling

if (session.ref.tabId !== desiredTabId) { const fresh = await call("browser_attach", { tabId: desiredTabId }); session = fresh.session; }
await call(tool, { session });

Try / catch

try { await call(tool, { session }); } catch (e) { if (e instanceof Error && e.message.includes("does not match the attached session")) { const { session: s } = await call("browser_attach", { tabId: session.ref.tabId }); await call(tool, { session: s }); } else throw e; }

Prevention

When it happens

Trigger: Calling a tool with { session: { sessionId, tabId } } where tabId differs from the tabId recorded when the session was created — e.g. copying a session object and swapping only the tabId.

Common situations: Scripts try to migrate a session to a new tab by editing the ref instead of creating a new session; a tab was closed and recreated with the caller guessing a new id under the same sessionId.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/870a0d2f658ddb50. Report an issue: GitHub.