jackwener/OpenCLI · error · Error

attach failed: ${lastError}${hint}

Error message

attach failed: ${lastError}${hint}

What it means

ensureAttached calls chrome.debugger.attach and, on failure, logs the URL/windowId context and throws 'attach failed: <lastError>'. If the error mentions chrome-extension://, a hint is appended suggesting another extension is interfering, since only one debugger client may attach per tab.

Source

Thrown at extension/src/cdp.ts:201

      }
    }
  }

  if (lastError) {
    // Log detailed diagnostics for debugging extension conflicts
    let finalUrl = 'unknown';
    let finalWindowId = 'unknown';
    try {
      const tab = await chrome.tabs.get(tabId);
      finalUrl = tab.url ?? 'undefined';
      finalWindowId = String(tab.windowId);
    } catch { /* tab gone */ }
    console.warn(`[opencli] attach failed for tab ${tabId}: url=${finalUrl}, windowId=${finalWindowId}, error=${lastError}`);

    const hint = lastError.includes('chrome-extension://')
      ? '. Tip: another Chrome extension may be interfering — try disabling other extensions'
      : '';
    throw new Error(`attach failed: ${lastError}${hint}`);
  }
  attached.add(tabId);

  try {
    await sendDebuggerCommand({ tabId }, 'Runtime.enable');
  } catch {
    // Some pages may not need explicit enable
  }

  // Restore network capture that the re-attach (detach + onDetach) tore down.
  // The detach always disables the CDP Network domain, so re-enable it and put
  // the accumulated capture state back unconditionally. Done last (after the
  // awaits above) so it wins over the onDetach handler's delete, which fires
  // while those awaits yield to the event loop.
  if (preservedNetworkCapture) {
    try {
      await sendDebuggerCommand({ tabId }, 'Network.enable');
      networkCaptures.set(tabId, preservedNetworkCapture);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Close DevTools for the target tab and retry
  2. Disable other Chrome extensions that use chrome.debugger, as the hint suggests
  3. Retry the command — transient races (tab mid-navigation) often succeed on a second attempt
  4. Check lastError details; if it says 'Another debugger already attached', find the conflicting client

Example fix

// before
attach failed: Another debugger is already attached to the tab
// after
// close DevTools / disable conflicting extension, then retry attach
Defensive patterns

Strategy: retry

Validate before calling

// cannot pre-check attach exclusivity; mitigate by closing DevTools and other debugger clients first
const dbg = await new Promise(r => chrome.debugger.getTargets(ts => r(ts.filter(t => t.tabId === tabId && t.attached))));
if (dbg.length) throw new Error('another debugger already attached');

Type guard

function isAlreadyAttachedError(msg: string): boolean {
  return /already attached|Another debugger/i.test(msg);
}

Try / catch

try {
  await cdp.ensureAttached(tabId);
} catch (e) {
  const m = (e as Error).message;
  if (m.startsWith('attach failed')) {
    if (isAlreadyAttachedError(m)) closeDevToolsOrConflictingExtension();
    await sleep(300); return retry();
  }
  throw e;
}

Prevention

When it happens

Trigger: chrome.debugger.attach rejects in ensureAttached — target closed mid-attach, another debugger/DevTools already attached, permission canceled by the user, or cross-extension contention.

Common situations: DevTools is open on the same tab (Chrome allows only one debugger); another automation extension (e.g. recording tools) holds the debugger; the tab navigated/closed during attach; user dismissed the 'started debugging' infobar permission.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/87b77025c2b829c3. Report an issue: GitHub.