dotnet / dotnet/maui

[iOS] CarouselViewHandler2 ArgumentOutOfRangeException from deferred realization during page teardown (not fixed by #26868)

Open
#36,754 5 comments 0 reactions 0 assignees View on GitHub
area-controls-collectionview platform/ios s/needs-info s/needs-repro
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.