[iOS] CarouselViewHandler2 ArgumentOutOfRangeException from deferred realization during page teardown (not fixed by #26868)
- Dominant language
- C#
- Stars
- 23.3k
- Forks
- 2k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 290
Description
### Description
CV2 (`CarouselViewHandler2`) crashes with `System.ArgumentOutOfRangeException` (`IndexMustBeLess`) when a page hosting a `CarouselView` is being torn down (navigating away) while CV2's **deferred realization** is still pending. This was previously discussed in #26171, which was closed as a duplicate of #26863 — but we still see this crash on MAUI **10.0.51–10.0.80**, which already contain the #26863 fix (#26868), so it appears to be a distinct bug.
Crash paths (production stack traces, iOS-only):
- `CarouselViewHandler2.MapPosition` / `MapCurrentItem` → `UpdateInitialPosition` → `SetPosition` → `ScrollToItem` executing inside an `NSAsyncActionDispatcher` continuation after teardown has begun
- `GetCell` → `DetermineCellReuseId` → `ListSource.get_Item`
Both read a stale carousel index during teardown. App-side synchronous guards cannot close it: a managed predicate cannot drain an already-applied `Position` write whose deferred native continuation fires later.
**Workaround:** pinning the affected `CarouselView` to the legacy CV1 handler (`Microsoft.Maui.Controls.Handlers.Items.CarouselViewHandler`) fully eliminates the crash, localizing the bug to CV2's deferred-realization path.
We checked the CV2 fixes merged since (#35532, #35848, #32141, #34013, #36681); none targets this teardown-time path.
### Steps to Reproduce
Not deterministically reproducible locally — it is a timing race between the deferred native continuation and page teardown (multiple simulator repro attempts failed). Real-world trigger in our app: a page with a `CarouselView` (ItemsSource of a few items) is navigated away from (push to a follow-up page) shortly after the carousel appears. Full production stack traces available on request.
### Link to public reproduction project repository
_No response_
### Version with bug
10.0.80
### Is this a regression from previous behavior?
Yes, this used to work in .NET 8 (CV1 handler)
### Last version that worked well
Unknown/Other
### Affected platforms
iOS
### Affected platform versions
iOS 15.8 – 26.x
### Did you find any workaround?
Pin the affected CarouselView to the legacy CV1 handler: `handlers.AddHandler()`
### Relevant log output
```
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (IndexMustBeLess)
at Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2 ... ScrollToItem (via UpdateInitialPosition/SetPosition, NSAsyncActionDispatcher continuation)
at ... GetCell → DetermineCellReuseId → ListSource.get_Item
```
Contributor guide
Research direction
Start with CarouselViewHandler2.MapPosition and MapCurrentItem, then trace UpdateInitialPosition, SetPosition, ScrollToItem, GetCell, and DetermineCellReuseId through the deferred native continuation and page teardown lifecycle. Reproduce or instrument navigation away from a page with a CarouselView, and consider the work done when the stale index no longer reaches these paths and the teardown scenario no longer throws.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, ios
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100