can1357/oh-my-pi · error · Error
No tab with id ${ref.tabId}
Error message
No tab with id ${ref.tabId} What it means
RelayBridge's #enableSessionRuntime looks up the tab registered for a relay tab reference before enabling the CDP runtime on it. If the tab map no longer contains ref.tabId (the tab was closed, the extension service worker restarted and lost its registry, or the reference is stale), it restores the previous runtimeState and throws Error(`No tab with id <id>`).
Source
Thrown at packages/coding-agent/src/tools/browser/relay/bridge.ts:484
this.#reply(conn, msg, {});
} catch (err) {
this.#replyError(conn, msg, err instanceof Error ? err.message : String(err));
}
}
/**
* Drive the shared root `Runtime.enable` for a session and replay the live
* contexts to it. Rejects if the root cycle fails so every joined caller
* observes the failure instead of a spurious success.
*/
async #enableSessionRuntime(conn: CdpConnection, sessionId: string, ref: SessionRef): Promise<void> {
const prev = ref.runtimeState;
const epoch = ++ref.runtimeEpoch;
ref.runtimeState = "enabled";
const tab = this.#tabs.get(ref.tabId);
if (!tab) {
ref.runtimeState = prev;
throw new Error(`No tab with id ${ref.tabId}`);
}
try {
await this.#ensureRuntimeEnabled(tab);
// A disable or newer enable may have taken ownership while the root
// RPC was in flight; only the latest enable may replay or roll back.
if (conn.sessions.get(sessionId) === ref && ref.runtimeEpoch === epoch && ref.runtimeState === "enabled") {
this.#replayRuntimeContexts(conn, sessionId, ref, tab);
}
} catch (err) {
if (ref.runtimeEpoch === epoch) {
ref.runtimeState = prev;
ref.runtimeContexts.clear();
}
throw err;
}
}
async #ensureRuntimeEnabled(tab: TabState): Promise<void> {View on GitHub (pinned to 9690622007)
Solutions
- Re-list tabs (query the relay for current tabs) and re-resolve the target tab to get a fresh, valid tabId before enabling
- Catch the error and treat the tab as gone: reopen or re-attach to a new tab
- Keep tab refs short-lived; re-acquire refs after any extension reload or Chrome restart
- If the service worker restarts frequently, pin the extension or check Chrome's service-worker lifetime settings
Example fix
// before
await bridge.enable(tabRef, session);
// after
try {
await bridge.enable(tabRef, session);
} catch (err) {
if (err.message.startsWith("No tab with id")) {
tabRef = await bridge.resolveTab(currentTabId); // refresh stale ref
await bridge.enable(tabRef, session);
} else throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify the tab still exists before enabling a session on it
const currentTabs = await bridge.listTabs();
if (!currentTabs.some(t => t.id === ref.tabId)) {
ref = await bridge.resolveTab(await pickTargetTab()); // re-acquire fresh ref
} Type guard
function tabExists(ref: { tabId: number }, tabs: { id: number }[]): boolean {
return tabs.some(t => t.id === ref.tabId);
} Try / catch
try {
await bridge.enable(ref, session);
} catch (err) {
if (err instanceof Error && /^No tab with id \d+$/.test(err.message)) {
ref = await refreshTabRef(); // tab closed or extension restarted
await bridge.enable(ref, session);
} else throw err;
} Prevention
- Re-list tabs after any user interaction that may close/reopen tabs
- Re-acquire tab refs whenever the extension service worker restarts (Chrome update, manual reload)
- Keep sessions short-lived; don't cache tab refs across long idle periods
- Handle tab-closed lifecycle events to invalidate refs proactively
When it happens
Trigger: Enabling a session on a tab ref whose underlying tab was closed or navigated away before enable; Chrome extension service worker restart clearing the in-memory tab registry; a stale tabId reused after Chrome renumbered tabs.
Common situations: Agent tool call targeting a tab the user closed mid-task; relay extension reloaded (Chrome update, manual reload) invalidating previously issued tab refs; long-running sessions holding tab IDs across extension lifecycles.
Related errors
- autoStarted ? `omp browser relay is serving at ${cdpUrl} but
- Extension failed, blocking execution: ${String(err)}
- Hook failed, blocking execution: ${String(err)}
- Unknown security scan: ${scanId}
- Unknown security finding: ${findingId}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c2c1d17c89c8b7e3.
Report an issue: GitHub.