jackwener/OpenCLI · error · Error
No targetId for tab ${tabId} — page may have been closed
Error message
No targetId for tab ${tabId} — page may have been closed What it means
resolveTargetId (extension/src/identity.ts:28) maps an internal Chrome tabId to the CDP targetId that serves as the page's cross-layer identity. On cache miss it refreshes all mappings from chrome.debugger.getTargets(); if the tab still has no 'page'-type target, it throws rather than guessing. This is a hard signal that the tab no longer exists or is not a debuggable page.
Source
Thrown at extension/src/identity.ts:28
* - Miss triggers full refresh; refresh miss → hard error (no guessing)
*/
const targetToTab = new Map<string, number>();
const tabToTarget = new Map<number, string>();
/**
* Resolve targetId for a given tabId.
* Returns cached value if available; on miss, refreshes from chrome.debugger.getTargets().
* Throws if no targetId can be found (page may have been destroyed).
*/
export async function resolveTargetId(tabId: number): Promise<string> {
const cached = tabToTarget.get(tabId);
if (cached) return cached;
await refreshMappings();
const result = tabToTarget.get(tabId);
if (!result) throw new Error(`No targetId for tab ${tabId} — page may have been closed`);
return result;
}
/**
* Resolve tabId for a given targetId.
* Returns cached value if available; on miss, refreshes from chrome.debugger.getTargets().
* Throws if no tabId can be found — never falls back to guessing.
*/
export async function resolveTabId(targetId: string): Promise<number> {
const cached = targetToTab.get(targetId);
if (cached !== undefined) return cached;
await refreshMappings();
const result = targetToTab.get(targetId);
if (result === undefined) throw new Error(`Page not found: ${targetId} — stale page identity`);
return result;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Wrap the call in try/catch and treat the error as 'page gone': abandon or re-open the page and obtain a fresh tabId.
- Before calling, verify the tab still exists via chrome.tabs.get(tabId).
- If the tab was closed unintentionally, reopen it (chrome.tabs.create with the recorded URL) and re-resolve.
- Don't cache tabIds across browser restarts; persist targetId/URL and re-resolve at session start.
- Check that the target type is 'page' — service workers and devtools targets are intentionally excluded from the mapping.
Example fix
// before
const targetId = await resolveTargetId(tabId);
// after
let targetId: string;
try { targetId = await resolveTargetId(tabId); }
catch (e) {
const tab = await chrome.tabs.create({ url: savedUrl });
targetId = await resolveTargetId(tab.id!);
} Defensive patterns
Strategy: try-catch
Validate before calling
let tabExists = true;
try { await chrome.tabs.get(tabId); } catch { tabExists = false; }
if (!tabExists) throw new PageGoneError(tabId); Try / catch
try {
targetId = await resolveTargetId(tabId);
} catch (err) {
if (err instanceof Error && /No targetId for tab/.test(err.message)) {
return handlePageClosed(tabId); // abandon, or chrome.tabs.create and re-resolve
}
throw err;
} Prevention
- Listen to chrome.tabs.onRemoved and drop tabIds from your state as soon as tabs close.
- Don't persist raw tabIds across browser restarts; store URLs and re-open instead.
- Verify with chrome.tabs.get before long-running operations on a previously captured tabId.
- Remember only 'page' targets with tabIds are mapped — service workers/devtools will always throw here.
When it happens
Trigger: Calling resolveTargetId after the tab was closed (user or window.close), for a tab id that never existed, for non-page targets (devtools, extension pages, service workers have no tabId mapping), or a crash of the renderer that destroyed the target.
Common situations: Long-running automation holding a tabId across a user closing the tab; driving a tab opened in another window the user dismissed; Chrome restarted and tab ids changed; passing a stale tabId from persisted state after browser relaunch.
Related errors
- Tab ${tabId} no longer exists
- Page not found: ${targetId} — stale page identity
- bound_tab_gone
- Requires up-to-date Chrome extension (nativeClick).
- Cannot create ${role} tab group without tabs
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ace3d51b731aa407.
Report an issue: GitHub.