dotnet/maui · error · ArgumentException
Index '{Math.Max(e.NewStartingIndex, e.OldStartingIndex)}' i
Error message
Index '{Math.Max(e.NewStartingIndex, e.OldStartingIndex)}' is greater than the number of rows '{lastIndex}'. What it means
Thrown by the iOS ListViewRenderer when a NotifyCollectionChangedEventArgs reports a NewStartingIndex or OldStartingIndex greater than the current number of rows in the section (only checked for RetainElement strategy and non-grouped lists). This indicates the bound ObservableCollection raised a change event whose index is inconsistent with the actual row count.
Source
Thrown at src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ListViewRenderer.cs:590
{
var exArgs = e as NotifyCollectionChangedEventArgsEx;
if (exArgs != null)
_dataSource.Counts[section] = exArgs.Count;
// This means the UITableView hasn't rendered any cells yet
// so there's no need to synchronize the rows on the UITableView
if (Control.IndexPathsForVisibleRows == null && e.Action != NotifyCollectionChangedAction.Reset)
return;
var groupReset = resetWhenGrouped && Element.IsGroupingEnabled;
// We can't do this check on grouped lists because the index doesn't match the number of rows in a section.
// Likewise, we can't do this check on lists using RecycleElement because the number of rows in a section will remain constant because they are reused.
if (!groupReset && Element.CachingStrategy == ListViewCachingStrategy.RetainElement)
{
var lastIndex = Control.NumberOfRowsInSection(section);
if (e.NewStartingIndex > lastIndex || e.OldStartingIndex > lastIndex)
throw new ArgumentException(
$"Index '{Math.Max(e.NewStartingIndex, e.OldStartingIndex)}' is greater than the number of rows '{lastIndex}'.");
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex == -1 || groupReset)
goto case NotifyCollectionChangedAction.Reset;
InsertRows(e.NewStartingIndex, e.NewItems.Count, section);
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldStartingIndex == -1 || groupReset)
goto case NotifyCollectionChangedAction.Reset;
DeleteRows(e.OldStartingIndex, e.OldItems.Count, section);View on GitHub (pinned to f377ff1c5e)
Solutions
- Marshal all collection mutations to the main/UI thread.
- Ensure INotifyCollectionChanged indices always match the post-mutation list state (use standard ObservableCollection).
- Prefer ListViewCachingStrategy.RecycleElement or grouping if index semantics differ, but first fix the source of bad indices.
- Validate that NewStartingIndex/OldStartingIndex are within [0, Count] before raising CollectionChanged in custom collections.
Example fix
// before — mutating off thread task.ContinueWith(_ => observableCollection.Insert(10, item)); // after MainThread.BeginInvokeOnMainThread(() => observableCollection.Insert(index, item));
Defensive patterns
Strategy: validation
Validate before calling
// Marshal collection mutations to the main thread and validate indices
MainThread.BeginInvokeOnMainThread(() =>
{
if (index >= 0 && index <= collection.Count)
collection.Insert(index, item);
}); Prevention
- Always mutate the bound ObservableCollection on the main thread.
- Use standard ObservableCollection to guarantee correct indices.
- Validate NewStartingIndex/OldStartingIndex against Count in custom collections.
When it happens
Trigger: An ObservableCollection<T> raises CollectionChanged with a starting index that exceeds the section's row count. Manipulating the underlying list from a background thread (cross-thread mutation). Calling collection.Move/Insert with a stale index. A custom INotifyCollectionChanged implementation emitting bad indices.
Common situations: Mutating the bound collection off the main thread. Batch operations that remove and re-add with mismatched indices. Wrapping a list with a custom notifier that miscalculates indices. Race between source mutation and renderer measurement.
Related errors
- indexPath
- Index '{Math.Max(e.NewStartingIndex, e.OldStartingIndex)}' i
- Implement INativeElementView on cell renderer: {ContentCell.
- No UIViewController found to present.
- Header cells do not support context actions
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/23268ee5c43b4fd9.
Report an issue: GitHub.