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
- Use the exact subscriptionId returned by the subscribe call.
- Track unsubscribe state so you don't call it twice with the same id.
- 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
- Track live subscription ids in a Set
- Make unsubscribe idempotent by swallowing not-found
- Re-subscribe after restarts
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
- browser_tab_activate requires tabId or session.
- browser_tab_close requires tabId or session.
- browser_navigate could not resolve an active tab.
- Chrome login import job was not found: ${jobId}
- Unknown browser automation tool: ${name}
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/ad5022131d738536.
Report an issue: GitHub.