A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
fix(vue-router): prevent out-of-bounds index when popping across tabs (#31148)
Issue number: resolves #29413 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? In `viewStacks.ts`, `unmountLeavingViews` and `mountIntermediaryViews` walk the outlet's view stack using `startIndex - delta` (or `startIndex + delta`) as the loop end, with no bound on `viewStack.length`. `delta` comes from the popstate event's history delta. Apps that mount `<ion-tabs>` at the root with no outer `<ion-router-outlet>` only have one outlet registered, so `usingLinearNavigation` is true and these helpers actually run. Each tab switch adds a browser history entry but reuses existing view items, so `|delta|` can easily exceed the stack depth above the entering view. The loop then reads `viewStack[i]` as `undefined` and throws `TypeError: viewItem is undefined` from `viewItem.mount = false`. The navigation aborts mid-transition, which is what surfaces the secondary `enteringEl is undefined` warning and leaves that route stuck ## What is the new behavior? Both helpers bail when the entering view item isn't in the stack (`startIndex === -1`) and clamp the loop end to `Math.min(viewStack.length, ...)`, so a delta that overruns the stack stops at the last real view item instead of indexing past the end. A new Vitest spec at `packages/vue/test/base/tests/unit/tabs-single-outlet.spec.ts` mounts `<ion-tabs>` as the app root with flat routes, builds up history across tabs and sub-pages, then calls `router.go(-6)`. Without the fix the spec catches the unhandled `TypeError` from `viewStacks.ts` ## Does this introduce a breaking change? - [ ] Yes - [X] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Main-branch counterpart of #31145. Source change is byte-identical to the source portion of that PR so the merge back into `major-9.0` collapses to a no-op. There are no tests here, but there are in the v9 version because the v9 testing setup is better.
S
Shane committed
c88c0de3ade92469fa1f37e1b8220911adf113e0
Parent: 7334144
Committed by GitHub <noreply@github.com>
on 5/15/2026, 1:31:28 PM