{"record":{"id":"6a07a91010ab1e59","repo":"ionic-team/ionic-framework","slug":"navigation-stack-needs-at-least-one-root-page","errorCode":null,"errorMessage":"navigation stack needs at least one root page","messagePattern":"navigation stack needs at least one root page","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/src/components/nav/nav.tsx","lineNumber":829,"sourceCode":"        const view = this.views[i];\n        if (view !== undefined && view !== enteringView && view !== leavingView) {\n          destroyQueue.push(view);\n        }\n      }\n      // default the direction to \"back\"\n      opts.direction ??= 'back';\n    }\n\n    const finalNumViews = this.views.length + (insertViews?.length ?? 0) - (removeCount ?? 0);\n    assert(finalNumViews >= 0, 'final balance can not be negative');\n    if (finalNumViews === 0) {\n      printIonWarning(\n        `[ion-nav] - You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`,\n        this,\n        this.el\n      );\n\n      throw new Error('navigation stack needs at least one root page');\n    }\n\n    // At this point the transition can not be rejected, any throw should be an error\n    // Insert the new views in the stack.\n    if (insertViews) {\n      // add the views to the\n      let insertIndex = ti.insertStart!;\n      for (const view of insertViews) {\n        this.insertViewAt(view, insertIndex);\n        insertIndex++;\n      }\n\n      if (ti.enteringRequiresTransition) {\n        // default to forward if not already set\n        opts.direction ??= 'forward';\n      }\n    }\n","sourceCodeStart":811,"sourceCodeEnd":847,"githubUrl":"https://github.com/ionic-team/ionic-framework/blob/625f9c38ad8c5db8a6c12df5c1622a36da90f9e3/core/src/components/nav/nav.tsx#L811-L847","documentation":"Thrown in IonNav.postViewInit() when the transition would leave the stack with zero views (`finalNumViews === 0`). IonNav requires at least one root page at all times, so removing the last page is rejected. A printIonWarning is emitted first explaining that pop() was likely called too many times; the throw then rejects the transition.","triggerScenarios":"Calling `pop()` on a nav whose stack has exactly one view; `popToRoot()` when the root itself is the only view and the math zeroes it out; `removeIndex(0, n)` or `setPages([])`/`setRoot` flows that compute to zero remaining views; calling pop more times than pages were pushed.","commonSituations":"Hardware/browser back button firing pop() repeatedly until the stack is empty; calling pop() inside an effect/loop without a length guard; setting root to nothing; race conditions where multiple pops queue against a shrinking stack.","solutions":["Guard pops with `await nav.getLength() > 1` (or `nav.canGoBack()`) before calling `pop()`/`popToRoot()`.","For back-button handlers, return early (or call `navigator.app.exitApp()` / app lifecycle) when only the root remains.","Avoid `setPages([])` — always keep at least one page; use `setRoot(RootPage)` to reset.","Throttle/debounce rapid pop() calls so queued transitions do not overshoot."],"exampleFix":"// before\nbackButton.subscribe(() => nav.pop());\n// after\nbackButton.subscribe(async () => {\n  if (await nav.canGoBack()) {\n    await nav.pop();\n  } else {\n    App.exitApp();\n  }\n});","handlingStrategy":"validation","validationCode":"// Guard pops so the stack never reaches zero\nif (await nav.canGoBack()) {\n  await nav.pop();\n} else {\n  // root is showing — handle app-level back (e.g. exit) instead\n  App.exitApp();\n}","typeGuard":null,"tryCatchPattern":"try {\n  await nav.pop();\n} catch (e) {\n  if ((e as Error).message === 'navigation stack needs at least one root page') {\n    // stack would be empty; ensure a root exists\n    await nav.setRoot(HomePage);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always check canGoBack()/getLength() > 1 before popping in back handlers.","Never call setPages([]) — always keep at least one root page.","Debounce rapid back-button presses to avoid queued overshoot pops."],"tags":["core","ion-nav","navigation","lifecycle"],"backgroundTag":null,"analyzedSha":"625f9c38ad8c5db8a6c12df5c1622a36da90f9e3","analyzedAt":"2026-08-12T16:00:25.413Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}