jackwener/OpenCLI · error · Error
Cannot debug tab ${tabId}: URL is ${tab.url ?? 'unknown'}
Error message
Cannot debug tab ${tabId}: URL is ${tab.url ?? 'unknown'} What it means
cdp.ensureAttached verifies the target tab's URL is debuggable before attaching the Chrome debugger; URLs like chrome://, chrome-extension://, file://, or edge:// cannot be debugged via chrome.debugger, so the library throws and invalidates any cached attach state. This prevents confusing low-level debugger errors later.
Source
Thrown at extension/src/cdp.ts:119
} finally {
if (timer !== undefined) clearTimeout(timer);
}
}
/** Check if a URL can be attached via CDP — only allow http(s) and blank pages. */
function isDebuggableUrl(url?: string): boolean {
if (!url) return true; // empty/undefined = tab still loading, allow it
return url.startsWith('http://') || url.startsWith('https://') || url === 'about:blank' || url.startsWith('data:');
}
export async function ensureAttached(tabId: number, aggressiveRetry: boolean = false): Promise<void> {
// Verify the tab URL is debuggable before attempting attach
try {
const tab = await chrome.tabs.get(tabId);
if (!isDebuggableUrl(tab.url)) {
// Invalidate cache if previously attached
attached.delete(tabId);
throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? 'unknown'}`);
}
} catch (e) {
// Re-throw our own error, catch only chrome.tabs.get failures
if (e instanceof Error && e.message.startsWith('Cannot debug tab')) throw e;
attached.delete(tabId);
throw new Error(`Tab ${tabId} no longer exists`);
}
if (attached.has(tabId)) {
// Verify the debugger is still actually attached by sending a harmless command
try {
await sendDebuggerCommand({ tabId }, 'Runtime.evaluate', {
expression: '1', returnByValue: true,
}, CDP_PROBE_TIMEOUT_MS);
return; // Still attached and working
} catch {
// Stale cache entry — need to re-attach
attached.delete(tabId);View on GitHub (pinned to 49907e53dc)
Solutions
- Navigate the tab to an http(s) URL before running CDP commands
- Target a different tab that shows web content (opencli browser tab list to pick one)
- If file:// access is required, enable 'Allow access to file URLs' — note the library still treats file:// as non-debuggable, so move the page to a local http server
- Add a pre-check in your script that skips non-http(s) tabs
Example fix
// before await cdp.evaluate(tabId, 'document.title'); // tab.url === 'chrome://version' // after const tab = await chrome.tabs.get(tabId); if (/^https?:/.test(tab.url ?? '')) await cdp.evaluate(tabId, 'document.title');
Defensive patterns
Strategy: validation
Validate before calling
const tab = await chrome.tabs.get(tabId);
if (!tab.url || !/^https?:|^about:blank|^data:/i.test(tab.url)) {
throw new Error(`skip CDP: non-debuggable url ${tab.url}`);
} Type guard
function canAttach(url?: string): url is string {
return !url || url.startsWith('http://') || url.startsWith('https://') || url === 'about:blank' || url.startsWith('data:');
} Try / catch
try {
await cdp.evaluate(tabId, expr);
} catch (e) {
const m = (e as Error).message;
if (m.startsWith('Cannot debug tab')) console.warn('navigate to http(s) first:', m);
else throw e;
} Prevention
- Filter target tabs by /^https?:/ before any CDP call
- Skip chrome:// and extension pages in automation loops
- Serve local files over http://localhost instead of file://
- Attach after the page finishes loading to avoid transient URL states
When it happens
Trigger: Any CDP-backed operation (handleCdp, evaluate, screenshot, setFileInputFiles, ensureFrameTarget, insertText) targets a tabId whose current tab.url fails isDebuggableUrl.
Common situations: Automation pointed at the active tab after the user opened chrome://settings; screenshotting a Chrome Web Store page; running evaluate against a file:// page; new-tab page focused during a command.
Related errors
- Bound tab for session "${session.session}" is not debuggable
- bound_tab_not_debuggable
- Tab ${tabId} no longer exists
- attach failed: ${lastError}${hint}
- No iframe target found for frame ${frameId}${targetUrl ? ` (
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3b0f7eb409a1a774.
Report an issue: GitHub.