garrytan/gstack · warning · Error

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

Error message

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

What it means

Frame-context guard in the goto case at write-commands.ts:145. Navigation targets the top-level page, but session.getFrame() is non-null — meaning a subframe was selected via the 'frame' command. goto on a frame-scoped session is disallowed to prevent accidentally navigating only a subframe.

Source

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

    // Follow prompts
    '[class*="follow-us"]', '[class*="social-follow"]',
  ],
};

export async function handleWriteCommand(
  command: string,
  args: string[],
  session: TabSession,
  bm: BrowserManager
): Promise<string> {
  const page = session.getPage();
  // Frame-aware target for locator-based operations (click, fill, etc.)
  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()}`;
    }

View on GitHub (pinned to 94993f7401)

Solutions

  1. Run 'frame main' to reset to the top-level page before issuing goto
  2. Reorder the script so all top-level navigations happen before entering any frame
  3. Check session.getFrame() === null in automation before calling goto

Example fix

// before (in subframe context)
await handleWriteCommand('goto', [url], session, bm)  // throws
// after
await handleWriteCommand('frame', ['main'], session, bm)
await handleWriteCommand('goto', [url], session, bm)
Defensive patterns

Strategy: validation

Validate before calling

function canNavigate(session: { getFrame(): unknown }): boolean {
  return session.getFrame() === null
}
// Usage: if (!canNavigate(session)) await handleWriteCommand('frame', ['main'], session, bm)

Type guard

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

Prevention

When it happens

Trigger: Running the sequence: 'frame <iframe-selector>' (or frame <id>) followed by 'goto <url>' without resetting to the main frame first.

Common situations: A script drills into an iframe to fill a field, then continues to the next navigation step without exiting the frame context.

Related errors


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