musistudio/claude-code-router · error · Error

Browser event subscription was not found: ${subscriptionId}

Error message

Browser event subscription was not found: ${subscriptionId}

What it means

Thrown by requireSubscription when browser event tool calls reference a subscriptionId that is not present in the MCP server's subscription map. Subscriptions are registered by an event-subscribe tool and keyed by the id it returns; unknown or evicted ids fail here.

Source

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

      timedOut: matched.length === 0
    };
  }

  private unsubscribeEvents(args: Record<string, unknown>): unknown {
    const subscription = this.requireSubscription(args);
    subscription.unsubscribe();
    this.subscriptions.delete(subscription.subscriptionId);
    return {
      ok: true,
      subscriptionId: subscription.subscriptionId
    };
  }

  private requireSubscription(args: Record<string, unknown>): EventSubscription {
    const subscriptionId = requiredString(args.subscriptionId, "Browser event subscriptionId is required.");
    const subscription = this.subscriptions.get(subscriptionId);
    if (!subscription) {
      throw new Error(`Browser event subscription was not found: ${subscriptionId}`);
    }
    return subscription;
  }

  private requestHandoff(args: Record<string, unknown>): unknown {
    const session = this.resolveOptionalSession(args);
    const sessionId = session?.ref.sessionId || readString(args.browserSessionId);
    const tabId = readString(args.tabId) || session?.ref.tabId;
    const handoff = builtInBrowserService.requestAutomationHandoff({
      kind: readHandoffKind(args.kind),
      message: readString(args.message),
      reason: requiredString(args.reason, "browser_handoff_request requires reason."),
      sessionId,
      tabId
    });
    return {
      ...humanHelpResultFields(handoff),
      state: browserWindowState()

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Use the exact subscriptionId returned by the subscribe call.
  2. Track unsubscribe state so you don't call it twice with the same id.
  3. After a restart or tab close, re-subscribe to obtain fresh ids.

Example fix

// before
await call("browser_events_unsubscribe", { subscriptionId });
await call("browser_events_unsubscribe", { subscriptionId }); // second call throws

// after
await call("browser_events_unsubscribe", { subscriptionId });
activeSubscriptions.delete(subscriptionId); // guard against double unsubscribe
Defensive patterns

Strategy: try-catch

Validate before calling

if (!activeSubscriptions.has(subscriptionId)) throw new Error("subscription already removed or unknown");
await call("browser_events_unsubscribe", { subscriptionId });
activeSubscriptions.delete(subscriptionId);

Try / catch

try { await call("browser_events_unsubscribe", { subscriptionId }); } catch (e) { if (e instanceof Error && e.message.includes("subscription was not found")) { /* idempotent cleanup — ignore */ } else throw e; }

Prevention

When it happens

Trigger: Calling an event-unsubscribe or event-poll tool with a subscriptionId that was never registered, was already unsubscribed, or was lost when its tab/session was cleaned up.

Common situations: Unsubscribing twice with the same id; using a subscription id after the app restarted; the subscription's tab closed and cascade-removed it.

Related errors


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