dotnet/wpf · error · ArgumentOutOfRangeException

index

Error message

index

What it means

During an asynchronous binding get, if PropertyPathWorker.GetValue returns the sentinel IListIndexOutOfRange, ClrBindingWorker throws ArgumentOutOfRangeException("index"). This surfaces when an async data binding indexes into an IList with an out-of-bounds index.

Solutions

  1. Guard the bound index against the collection Count (e.g. bind via a converter or computed property that clamps the index)
  2. Refresh/re-evaluate the binding after collection changes (INotifyCollectionChanged keeps indices valid)
  3. Use an indexer property that returns null/default for out-of-range indices instead of raw list indexing

Example fix

// before
Text="{Binding AsyncValue[LastIndex]}" // LastIndex may exceed Count
// after
public object SafeItem(int i) => i >= 0 && i < Items.Count ? Items[i] : null;
Defensive patterns

Strategy: validation

Validate before calling

object SafeGet(IList list, int i) => (i >= 0 && i < list.Count) ? list[i] : null;

Type guard

bool IndexIsValid(IList list, int i) => list != null && i >= 0 && i < list.Count;

Try / catch

try { /* binding evaluation */ } catch (ArgumentOutOfRangeException) { /* index out of range on source list; refresh binding */ }

Prevention

When it happens

Trigger: An async {Binding Path=[i]} where the source list is shorter than i+1 at evaluation time (e.g. list shrunk or index computed before population).

Common situations: Binding to collection indexes that change as items are added/removed asynchronously; race between background updates and binding evaluation; off-by-one on count-based index bindings.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/2895ffa0c5370f55. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/ClrBindingWorker.cs:634

            AsyncGetValueRequest pendingGetValueRequest = (AsyncGetValueRequest)GetValue(Feature.PendingGetValueRequest, null);
            pendingGetValueRequest?.Cancel();

            // issue the new request
            pendingGetValueRequest =
                new AsyncGetValueRequest(item, name, ParentBinding.AsyncState,
                                DoGetValueCallback, CompleteGetValueCallback,
                                this, level);
            SetValue(Feature.PendingGetValueRequest, pendingGetValueRequest);
            Engine.AddAsyncRequest(TargetElement, pendingGetValueRequest);
        }

        private static object OnGetValueCallback(AsyncDataRequest adr)
        {
            AsyncGetValueRequest request = (AsyncGetValueRequest)adr;
            ClrBindingWorker worker = (ClrBindingWorker)request.Args[0];
            object value = worker.PW.GetValue(request.SourceItem, (int)request.Args[1]);
            if (value == PropertyPathWorker.IListIndexOutOfRange)
                throw new ArgumentOutOfRangeException("index");
            return value;
        }

        private static object OnCompleteGetValueCallback(AsyncDataRequest adr)
        {
            AsyncGetValueRequest request = (AsyncGetValueRequest)adr;
            ClrBindingWorker worker = (ClrBindingWorker)request.Args[0];

            DataBindEngine engine = worker.Engine;
            engine?.Marshal(CompleteGetValueLocalCallback, request);

            return null;
        }

        private static object OnCompleteGetValueOperation(object arg)
        {
            AsyncGetValueRequest request = (AsyncGetValueRequest)arg;
            ClrBindingWorker worker = (ClrBindingWorker)request.Args[0];

View on GitHub (pinned to 81131a70a4)