SIGN IN SIGN UP

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.

0 0 48 TypeScript

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