garrytan/gstack · error · Error

Frame not found: ${target}

Error message

Frame not found: ${target}

What it means

After resolving the target via `--name`, `--url`, or an element selector/ref, the command checks whether a `Frame` was found (line 1029). If Playwright returned null — no frame matched the name/URL, or the resolved element had no content frame — the command throws, echoing the target. The command then sets the frame and clears refs only on success.

Source

Thrown at browse/src/meta-commands.ts:1029

      const page = bm.getPage();
      let frame: Frame | null = null;

      if (target === '--name') {
        if (!args[1]) throw new Error('Usage: frame --name <name>');
        frame = page.frame({ name: args[1] });
      } else if (target === '--url') {
        if (!args[1]) throw new Error('Usage: frame --url <pattern>');
        frame = page.frame({ url: new RegExp(escapeRegExp(args[1])) });
      } else {
        // CSS selector or @ref for the iframe element
        const resolved = await bm.resolveRef(target);
        const locator = 'locator' in resolved ? resolved.locator : page.locator(resolved.selector);
        const elementHandle = await locator.elementHandle({ timeout: 5000 });
        frame = await elementHandle?.contentFrame() ?? null;
        await elementHandle?.dispose();
      }

      if (!frame) throw new Error(`Frame not found: ${target}`);
      bm.setFrame(frame);
      bm.clearRefs();
      return `Switched to frame: ${frame.url()}`;
    }

    // ─── UX Audit ─────────────────────────────────────
    case 'ux-audit': {
      const page = bm.getPage();

      // Extract page structure for UX behavioral analysis
      // Agent interprets the data and applies Krug's 6 usability tests
      // Uses textContent (not innerText) to avoid layout computation on large DOMs
      const data = await page.evaluate(() => {
        const HEADING_CAP = 50;
        const INTERACTIVE_CAP = 200;
        const TEXT_BLOCK_CAP = 50;

        // Site ID: logo or brand element

View on GitHub (pinned to 94993f7401)

Solutions

  1. Verify the iframe exists with a `snapshot`/`text` first; wait for it if it loads lazily.
  2. Check the frame name/URL against the page's actual frames (e.g. via `cdp` or DOM inspection).
  3. Ensure the element ref points to an `<iframe>` element, not a wrapper `<div>`.
  4. For dynamic frames, retry after the iframe's network request settles.

Example fix

// before
browse frame @e5  // @e5 is a div, not an iframe
// after
browse frame iframe.widget
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort: confirm the iframe exists before switching.
const frames = bm.getPage().frames();
const matches = frames.filter(f => /* name/url/selector logic */);
if (matches.length === 0) throw new Error('No matching frame; page may still be loading');

Try / catch

try { await browse.frame(target); }
catch (err) {
  if (/Frame not found/.test(err.message)) {
    await bm.getPage().waitForLoadState('networkidle', { timeout: 3000 }).catch(() => {});
    return browse.frame(target); // single retry after settle
  }
  throw err;
}

Prevention

When it happens

Trigger: `browse frame --name nope`, `browse frame --url 'does-not-exist\.com'`, or `browse frame @e5` where `@e5` is not an `<iframe>`/has no content frame.

Common situations: iframes loading asynchronously (frame not yet present), wrong name/URL pattern, element ref pointing to a non-iframe, cross-origin frames Playwright cannot enumerate, or a frame that has navigated away.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/73e11b8cbafdfb5f. Report an issue: GitHub.