{"record":{"id":"613b4c7ca3e5b3b1","repo":"garrytan/gstack","slug":"cannot-use-goto-inside-a-frame-run-frame-main-f","errorCode":null,"errorMessage":"Cannot use goto inside a frame. Run 'frame main' first.","messagePattern":"Cannot use goto inside a frame\\. Run 'frame main' first\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"browse/src/write-commands.ts","lineNumber":145,"sourceCode":"    // Follow prompts\n    '[class*=\"follow-us\"]', '[class*=\"social-follow\"]',\n  ],\n};\n\nexport async function handleWriteCommand(\n  command: string,\n  args: string[],\n  session: TabSession,\n  bm: BrowserManager\n): Promise<string> {\n  const page = session.getPage();\n  // Frame-aware target for locator-based operations (click, fill, etc.)\n  const target = session.getActiveFrameOrPage();\n  const inFrame = session.getFrame() !== null;\n\n  switch (command) {\n    case 'goto': {\n      if (inFrame) throw new Error('Cannot use goto inside a frame. Run \\'frame main\\' first.');\n      const url = args[0];\n      if (!url) throw new Error('Usage: browse goto <url>');\n      // Clear loadedHtml BEFORE navigation — a timeout after the main-frame commit\n      // must not leave stale content that could resurrect on a later context recreation.\n      session.clearLoadedHtml();\n      const normalizedUrl = await validateNavigationUrl(url);\n      const response = await page.goto(normalizedUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });\n      const status = response?.status() || 'unknown';\n      return `Navigated to ${normalizedUrl} (${status})`;\n    }\n\n    case 'back': {\n      if (inFrame) throw new Error('Cannot use back inside a frame. Run \\'frame main\\' first.');\n      session.clearLoadedHtml();\n      await page.goBack({ waitUntil: 'domcontentloaded', timeout: 15000 });\n      return `Back → ${page.url()}`;\n    }\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L127-L163","documentation":"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.","triggerScenarios":"Running the sequence: 'frame <iframe-selector>' (or frame <id>) followed by 'goto <url>' without resetting to the main frame first.","commonSituations":"A script drills into an iframe to fill a field, then continues to the next navigation step without exiting the frame context.","solutions":["Run 'frame main' to reset to the top-level page before issuing goto","Reorder the script so all top-level navigations happen before entering any frame","Check session.getFrame() === null in automation before calling goto"],"exampleFix":"// before (in subframe context)\nawait handleWriteCommand('goto', [url], session, bm)  // throws\n// after\nawait handleWriteCommand('frame', ['main'], session, bm)\nawait handleWriteCommand('goto', [url], session, bm)","handlingStrategy":"validation","validationCode":"function canNavigate(session: { getFrame(): unknown }): boolean {\n  return session.getFrame() === null\n}\n// Usage: if (!canNavigate(session)) await handleWriteCommand('frame', ['main'], session, bm)","typeGuard":"function isMainFrameSession(s: { getFrame(): unknown }): boolean {\n  return s.getFrame() === null\n}","tryCatchPattern":null,"preventionTips":["Always issue 'frame main' before any navigation command","Structure scripts so all navigations precede frame entry","Assert getFrame() === null in automation before goto"],"tags":["frames","navigation","write-commands","session-state"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}