{"record":{"id":"3b0f7eb409a1a774","repo":"jackwener/OpenCLI","slug":"cannot-debug-tab-tabid-url-is-tab-url-un","errorCode":null,"errorMessage":"Cannot debug tab ${tabId}: URL is ${tab.url ?? 'unknown'}","messagePattern":"Cannot debug tab (.+?): URL is (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extension/src/cdp.ts","lineNumber":119,"sourceCode":"  } finally {\n    if (timer !== undefined) clearTimeout(timer);\n  }\n}\n\n/** Check if a URL can be attached via CDP — only allow http(s) and blank pages. */\nfunction isDebuggableUrl(url?: string): boolean {\n  if (!url) return true;  // empty/undefined = tab still loading, allow it\n  return url.startsWith('http://') || url.startsWith('https://') || url === 'about:blank' || url.startsWith('data:');\n}\n\nexport async function ensureAttached(tabId: number, aggressiveRetry: boolean = false): Promise<void> {\n  // Verify the tab URL is debuggable before attempting attach\n  try {\n    const tab = await chrome.tabs.get(tabId);\n    if (!isDebuggableUrl(tab.url)) {\n      // Invalidate cache if previously attached\n      attached.delete(tabId);\n      throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? 'unknown'}`);\n    }\n  } catch (e) {\n    // Re-throw our own error, catch only chrome.tabs.get failures\n    if (e instanceof Error && e.message.startsWith('Cannot debug tab')) throw e;\n    attached.delete(tabId);\n    throw new Error(`Tab ${tabId} no longer exists`);\n  }\n\n  if (attached.has(tabId)) {\n    // Verify the debugger is still actually attached by sending a harmless command\n    try {\n      await sendDebuggerCommand({ tabId }, 'Runtime.evaluate', {\n        expression: '1', returnByValue: true,\n      }, CDP_PROBE_TIMEOUT_MS);\n      return; // Still attached and working\n    } catch {\n      // Stale cache entry — need to re-attach\n      attached.delete(tabId);","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/extension/src/cdp.ts#L101-L137","documentation":"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.","triggerScenarios":"Any CDP-backed operation (handleCdp, evaluate, screenshot, setFileInputFiles, ensureFrameTarget, insertText) targets a tabId whose current tab.url fails isDebuggableUrl.","commonSituations":"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.","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"],"exampleFix":"// before\nawait cdp.evaluate(tabId, 'document.title');   // tab.url === 'chrome://version'\n// after\nconst tab = await chrome.tabs.get(tabId);\nif (/^https?:/.test(tab.url ?? '')) await cdp.evaluate(tabId, 'document.title');","handlingStrategy":"validation","validationCode":"const tab = await chrome.tabs.get(tabId);\nif (!tab.url || !/^https?:|^about:blank|^data:/i.test(tab.url)) {\n  throw new Error(`skip CDP: non-debuggable url ${tab.url}`);\n}","typeGuard":"function canAttach(url?: string): url is string {\n  return !url || url.startsWith('http://') || url.startsWith('https://') || url === 'about:blank' || url.startsWith('data:');\n}","tryCatchPattern":"try {\n  await cdp.evaluate(tabId, expr);\n} catch (e) {\n  const m = (e as Error).message;\n  if (m.startsWith('Cannot debug tab')) console.warn('navigate to http(s) first:', m);\n  else throw e;\n}","preventionTips":["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"],"tags":["cdp","debugging","chrome-extension","url-policy"],"backgroundTag":"tab-not-debuggable","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}