garrytan/gstack · warning · Error

Cannot use back inside a frame. Run 'frame main' first.

Error message

Cannot use back inside a frame. Run 'frame main' first.

What it means

Frame-context guard in the back case at write-commands.ts:159. page.goBack() operates on the top-level page; with a subframe selected (session.getFrame() !== null) it is refused. Same invariant as goto — navigation is a page-level operation.

Source

Thrown at browse/src/write-commands.ts:158

  const target = session.getActiveFrameOrPage();
  const inFrame = session.getFrame() !== null;

  switch (command) {
    case 'goto': {
      if (inFrame) throw new Error('Cannot use goto inside a frame. Run \'frame main\' first.');
      const url = args[0];
      if (!url) throw new Error('Usage: browse goto <url>');
      // Clear loadedHtml BEFORE navigation — a timeout after the main-frame commit
      // must not leave stale content that could resurrect on a later context recreation.
      session.clearLoadedHtml();
      const normalizedUrl = await validateNavigationUrl(url);
      const response = await page.goto(normalizedUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });
      const status = response?.status() || 'unknown';
      return `Navigated to ${normalizedUrl} (${status})`;
    }

    case 'back': {
      if (inFrame) throw new Error('Cannot use back inside a frame. Run \'frame main\' first.');
      session.clearLoadedHtml();
      await page.goBack({ waitUntil: 'domcontentloaded', timeout: 15000 });
      return `Back → ${page.url()}`;
    }

    case 'forward': {
      if (inFrame) throw new Error('Cannot use forward inside a frame. Run \'frame main\' first.');
      session.clearLoadedHtml();
      await page.goForward({ waitUntil: 'domcontentloaded', timeout: 15000 });
      return `Forward → ${page.url()}`;
    }

    case 'reload': {
      if (inFrame) throw new Error('Cannot use reload inside a frame. Run \'frame main\' first.');
      session.clearLoadedHtml();
      await page.reload({ waitUntil: 'domcontentloaded', timeout: 15000 });
      return `Reloaded ${page.url()}`;
    }

View on GitHub (pinned to 94993f7401)

Solutions

  1. Run 'frame main' before 'back'
  2. Order navigation commands (back/forward/goto/reload) before any frame entry
  3. Guard automation on session.getFrame() === null

Example fix

// before
await handleWriteCommand('back', [], session, bm)  // in frame → throws
// after
await handleWriteCommand('frame', ['main'], session, bm)
await handleWriteCommand('back', [], session, bm)
Defensive patterns

Strategy: validation

Validate before calling

function canNavigate(session: { getFrame(): unknown }): boolean {
  return session.getFrame() === null
}

Type guard

function isMainFrameSession(s: { getFrame(): unknown }): boolean {
  return s.getFrame() === null
}

Prevention

When it happens

Trigger: Running 'back' while a frame context is active (after 'frame <selector>' and before 'frame main').

Common situations: Automated flow enters an iframe, interacts, then tries to go back without resetting the frame.

Related errors


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