musistudio/claude-code-router · error · Error

This browser automation session is observeOnly and cannot mu

Error message

This browser automation session is observeOnly and cannot mutate browser state.

What it means

Thrown by assertCanMutate when a tool that changes browser state (navigate, close tab, click, type, etc.) is invoked with a session whose observeOnly flag is true. Observe-only sessions are limited to read-only operations like snapshots and screenshots by design.

Source

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

      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 {
    if (session?.observeOnly) {
      throw new Error("This browser automation session is observeOnly and cannot mutate browser state.");
    }
  }

  private removeSessionsForTab(tabId: string): void {
    for (const [sessionId, session] of this.sessions) {
      if (session.ref.tabId === tabId) {
        this.sessions.delete(sessionId);
      }
    }
    for (const [subscriptionId, subscription] of this.subscriptions) {
      if (subscription.ref?.tabId === tabId) {
        subscription.unsubscribe();
        this.subscriptions.delete(subscriptionId);
      }
    }
  }

  private subscribeEvents(args: Record<string, unknown>): unknown {

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Attach a non-observe-only session (observeOnly: false / omitted) for mutating operations.
  2. Split your workflow: read via the observe-only session, mutate via a full session.
  3. Check the session's observeOnly flag before dispatching mutating tools.

Example fix

// before
const { session } = await call("browser_attach", { tabId, observeOnly: true });
await call("browser_navigate", { url, session });

// after
const { session } = await call("browser_attach", { tabId });
await call("browser_navigate", { url, session });
Defensive patterns

Strategy: validation

Validate before calling

if (session.observeOnly && MUTATING_TOOLS.includes(toolName)) { const { session: rw } = await call("browser_attach", { tabId: session.ref.tabId }); session = rw; }
await call(toolName, { ...args, session });

Type guard

function isMutableSession(s: { observeOnly?: boolean } | undefined): boolean { return !s?.observeOnly; }

Try / catch

try { await call(toolName, args); } catch (e) { if (e instanceof Error && e.message.includes("observeOnly")) { /* attach a non-observe-only session and retry */ } else throw e; }

Prevention

When it happens

Trigger: Calling a mutating tool (e.g. browser_navigate, browser_tab_close) with a session that was attached with observeOnly: true.

Common situations: A monitoring/audit integration intentionally attaches read-only but a later step tries to act; the observeOnly flag defaulted on unexpectedly in session setup.

Related errors


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