musistudio/claude-code-router · error · Error

browser_tab_close requires tabId or session.

Error message

browser_tab_close requires tabId or session.

What it means

Thrown by the browser_tab_close MCP tool when no tab identifier can be resolved from args.tabId or an attached session. Closing requires an explicit target tab; the tool intentionally has no 'close active tab' implicit behavior.

Source

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

        await this.ensureBrowserOpen();
        const session = this.resolveOptionalSession(args);
        const tabId = readString(args.tabId) || session?.ref.tabId;
        if (!tabId) {
          throw new Error("browser_tab_activate requires tabId or session.");
        }
        const state = builtInBrowserService.selectAutomationTab(tabId);
        return {
          ...browserWindowState(state),
          tab: summarizeTab(requiredTabState(state, tabId), state.activeTabId)
        };
      }
      case "browser_tab_close": {
        await this.ensureBrowserOpen();
        const session = this.resolveOptionalSession(args);
        this.assertCanMutate(session);
        const tabId = readString(args.tabId) || session?.ref.tabId;
        if (!tabId) {
          throw new Error("browser_tab_close requires tabId or session.");
        }
        const state = builtInBrowserService.closeAutomationTab(tabId);
        this.removeSessionsForTab(tabId);
        return browserWindowState(state);
      }
      case "browser_navigate": {
        await this.ensureBrowserOpen();
        const session = this.resolveOptionalSession(args);
        this.assertCanMutate(session);
        const tabId = readString(args.tabId) || session?.ref.tabId || builtInBrowserService.getAutomationState().activeTabId;
        if (!tabId) {
          throw new Error("browser_navigate could not resolve an active tab.");
        }
        const url = requiredString(args.url, "browser_navigate requires url.");
        const waitUntil = normalizeWaitUntil(readString(args.waitUntil), "interactive");
        const readiness = await waitForReadiness(
          builtInBrowserService.getAutomationWebContents(tabId),
          waitUntil,

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Pass tabId explicitly in the tool arguments.
  2. Or pass the session ref obtained from the tool run that created the tab.
  3. Re-query tab state before closing to confirm the tab still exists and capture its id.

Example fix

// before
await call("browser_tab_close", {});

// after
await call("browser_tab_close", { tabId });
Defensive patterns

Strategy: validation

Validate before calling

if (!args.tabId && !args.session?.ref?.tabId) throw new Error("No tab to close — fetch state first");
await call("browser_tab_close", args);

Type guard

function hasTabTarget(args: unknown): boolean { const a = args as any; return Boolean(a?.tabId ?? a?.session?.ref?.tabId); }

Try / catch

try { await call("browser_tab_close", { tabId }); } catch (e) { if (e instanceof Error && e.message.includes("requires tabId or session")) { /* re-list tabs */ } throw e; }

Prevention

When it happens

Trigger: Calling tools/call with name='browser_tab_close' with empty arguments (no tabId, no session), or with a session whose ref failed to resolve.

Common situations: A script closes tabs in a loop after the tab was already closed and removed from the session map; the caller assumed the last-used tab would be implicit.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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