jackwener/OpenCLI · error · Error

No iframe target found for frame ${frameId}${targetUrl ? ` (

Error message

No iframe target found for frame ${frameId}${targetUrl ? ` (${targetUrl})` : ''}. Candidates: ${candidates || 'none'}

What it means

This error is thrown by resolveFrameTargetId in extension/src/cdp.ts:587 when ensureFrameTarget cannot find a CDP iframe target matching the requested frameId (or targetUrl) among the tab's Target.getTargets() results. The extension auto-attaches to iframes via Target.setAutoAttach with an iframe filter, then looks up the matching out-of-process iframe target to route frame-scoped CDP commands. If no candidate matches, it throws and includes all discovered iframe candidates in the message to aid debugging.

Source

Thrown at extension/src/cdp.ts:587

  const result = await sendDebuggerCommand({ tabId }, 'Target.getTargets').catch(() => null) as
    | { targetInfos?: Array<{ targetId?: string; id?: string; type?: string; url?: string }> }
    | null;
  const targets = result?.targetInfos ?? [];
  const frameTarget = targets.find((candidate) => {
    const candidateId = candidate.targetId || candidate.id;
    return candidate.type === 'iframe'
      && (
        candidateId === frameId
        || (!!targetUrl && candidate.url === targetUrl)
      );
  });
  const targetId = frameTarget?.targetId || frameTarget?.id;
  if (targetId) return targetId;
  const candidates = targets
    .filter((target) => target.type === 'iframe')
    .map((target) => `${target.targetId || target.id || '?'} ${target.url || ''}`)
    .join('; ');
  throw new Error(`No iframe target found for frame ${frameId}${targetUrl ? ` (${targetUrl})` : ''}. Candidates: ${candidates || 'none'}`);
}

export async function sendCommandInFrameTarget(
  tabId: number,
  frameId: string,
  method: string,
  params: Record<string, unknown> = {},
  aggressiveRetry: boolean = false,
  timeoutMs: number = CDP_COMMAND_TIMEOUT_MS,
  targetUrl?: string,
): Promise<unknown> {
  const targetId = await ensureFrameTarget(tabId, frameId, aggressiveRetry, targetUrl);
  const target = { targetId } as chrome.debugger.Debuggee;
  return sendDebuggerCommand(target, method, params, timeoutMs);
}

export async function insertText(
  tabId: number,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass the frame's exact URL as targetUrl so resolveFrameTargetId can match by URL, or re-resolve the frameId from Page.getFrameTree immediately before the call.
  2. Retry after a short delay to let Target.setAutoAttach register the iframe target (this is what aggressiveRetry is for).
  3. Verify with Target.getTargets (or the 'none' in the message) whether any iframe targets exist; if none, evaluate in the main tab context instead.
  4. If the call came from evaluateInFrame, ensure Runtime.enable was issued so execution-context tracking populated tabFrameContexts; clear stale context cache and retry.
  5. Check Chrome version/platform — some embedders (e.g. Electron, older Chromium) do not create OOPIF targets for same-site iframes.

Example fix

// before
await sendCommandInFrameTarget(tabId, frameId, 'Runtime.evaluate', { expression });
// after
const tree = await getFrameTree(tabId);
const frameUrl = tree?.frame?.childFrames?.find(f => f.frame.id === frameId)?.frame.url;
await sendCommandInFrameTarget(tabId, frameId, 'Runtime.evaluate', { expression }, true /* aggressiveRetry */, undefined, frameUrl);
Defensive patterns

Strategy: retry

Validate before calling

const tree = await cdp.getFrameTree(tabId);
const hasIframeTargets = (await cdp.getTargetInfos(tabId)).some(t => t.type === 'iframe');
if (!hasIframeTargets) throw new SkipError('page has no OOPIF iframe targets; evaluate in main frame');

Type guard

function isIframeTarget(t: { type?: string; targetId?: string; id?: string }): t is { type: 'iframe'; targetId: string } {
  return t.type === 'iframe' && typeof (t.targetId || t.id) === 'string';
}

Try / catch

try {
  await sendCommandInFrameTarget(tabId, frameId, method, params, true /* aggressiveRetry */, timeoutMs, frameUrl);
} catch (err) {
  if (err instanceof Error && /No iframe target found/.test(err.message)) {
    await sleep(250); // let Target.setAutoAttach register the OOPIF target
    return sendCommandInFrameTarget(tabId, frameId, method, params, true, timeoutMs, frameUrl);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling sendCommandInFrameTarget or evaluateInFrame with a frameId that has no matching out-of-process iframe target: the frame is same-process (no separate target created), the frame navigated/reloaded and its target id changed, Target.getTargets returned before the iframe target was registered, the frameId passed is a Chrome frameTree frameId rather than a CDP iframe targetId, or the page has no iframes at all.

Common situations: Automating a page whose iframes are same-origin (Chrome keeps them in-process, so no iframe target exists); racing a just-inserted iframe before auto-attach fires; stale cached frame references after SPA navigation; passing the wrong id field (e.g. webFrameTree frameId vs targetId); headless or restricted environments where Target.setDiscoverTargets fails silently.

Related errors


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