musistudio/claude-code-router · error · Error

Browser tab was not found: ${tabId}

Error message

Browser tab was not found: ${tabId}

What it means

Thrown at the start of the main browser automation tool run when args.tabId is provided but does not match any tab in the automation browser's state. It is an explicit-target validation: an unknown tab id is rejected immediately rather than creating a new tab.

Source

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

    await builtInBrowserService.openHidden(await loadAppConfig());
    return builtInBrowserService.getAutomationState();
  }

  private async ensureBrowserVisible(): Promise<BuiltInBrowserState> {
    await builtInBrowserService.open(await loadAppConfig());
    return builtInBrowserService.getAutomationState();
  }

  private async openSession(args: Record<string, unknown>): Promise<unknown> {
    let state = await this.ensureBrowserOpen();
    const url = readString(args.url);
    const requestedTabId = readString(args.tabId);
    const previousActiveTabId = state.activeTabId;
    let tabId = requestedTabId;
    let createdTab = false;

    if (tabId && !state.tabs.some((tab) => tab.id === tabId)) {
      throw new Error(`Browser tab was not found: ${tabId}`);
    }

    if (!tabId && url) {
      const tab = builtInBrowserService.createAutomationTab();
      tabId = tab.id;
      createdTab = true;
    } else if (!tabId) {
      tabId = state.activeTabId || builtInBrowserService.createAutomationTab().id;
      createdTab = !state.activeTabId;
    } else if (state.activeTabId !== tabId) {
      builtInBrowserService.selectAutomationTab(tabId);
    }

    if (!tabId) {
      throw new Error("Unable to resolve browser tab for automation session.");
    }

    const waitUntil = normalizeWaitUntil(readString(args.waitUntil), url ? "interactive" : "none");

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Re-fetch current tab state via browser_tab_list and use a live tabId.
  2. If you want a new tab, omit tabId and pass url so one is created.
  3. Handle this error in scripts by re-querying state instead of retrying the same stale id.

Example fix

// before
await call("browser_navigate", { url, tabId: cachedTabId }); // tab was closed

// after
const { tabs } = await call("browser_tab_list", {});
const live = tabs.find((t) => t.url.includes("example.com")) ?? tabs[0];
await call("browser_navigate", { url, tabId: live?.id });
Defensive patterns

Strategy: fallback

Validate before calling

const { tabs } = await call("browser_tab_list", {});
if (!tabs.some((t) => t.id === tabId)) { tabId = tabs[0]?.id; } // refresh stale id
await call("browser_navigate", { url, tabId });

Try / catch

try { await call("browser_navigate", { url, tabId }); } catch (e) { if (e instanceof Error && e.message.includes("Browser tab was not found")) { const { tabs } = await call("browser_tab_list", {}); await call("browser_navigate", { url, tabId: tabs[0]?.id }); } else throw e; }

Prevention

When it happens

Trigger: Passing a tabId from a stale prior session after tabs were closed, from a different browser instance, or a fabricated/mistyped id.

Common situations: Scripts cache tab ids across steps; a tab crashes or is closed mid-script between obtaining the id and using it; the automation browser was reopened resetting tab ids.

Related errors


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