abpframework/abp · error · IndexOutOfRangeException

targetIndex should be between 0 and {source.Count - 1}

Error message

targetIndex should be between 0 and {source.Count - 1}

What it means

Thrown by AbpListExtensions.MoveItem when targetIndex is outside [0, source.Count - 1]. MoveItem relocates the element matching selector to targetIndex via RemoveAt + Insert, so an out-of-range index is rejected up front with IndexOutOfRangeException. An empty list makes the valid range [0, -1], so any targetIndex throws.

Source

Thrown at framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs:155

    }

    public static void ReplaceOne<T>(this IList<T> source, T item, T replaceWith)
    {
        for (int i = 0; i < source.Count; i++)
        {
            if (Comparer<T>.Default.Compare(source[i], item) == 0)
            {
                source[i] = replaceWith;
                return;
            }
        }
    }

    public static void MoveItem<T>(this List<T> source, Predicate<T> selector, int targetIndex)
    {
        if (!targetIndex.IsBetween(0, source.Count - 1))
        {
            throw new IndexOutOfRangeException("targetIndex should be between 0 and " + (source.Count - 1));
        }

        var currentIndex = source.FindIndex(0, selector);
        if (currentIndex == targetIndex)
        {
            return;
        }

        var item = source[currentIndex];
        source.RemoveAt(currentIndex);
        source.Insert(targetIndex, item);
    }

    [NotNull]
    public static T GetOrAdd<T>([NotNull] this IList<T> source, Func<T, bool> selector, Func<T> factory)
    {
        Check.NotNull(source, nameof(source));

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Bound-check targetIndex before calling: targetIndex = Math.Clamp(targetIndex, 0, source.Count - 1) (only if Count > 0).
  2. Ensure the list is non-empty and targetIndex was computed from the current Count.
  3. Handle the empty-list case explicitly before attempting to move an item.

Example fix

// before
list.MoveItem(x => x.Id == id, newIndex); // throws if newIndex out of range

// after
if (list.Count == 0) return;
list.MoveItem(x => x.Id == id, Math.Clamp(newIndex, 0, list.Count - 1));
Defensive patterns

Strategy: validation

Validate before calling

// Bound-check before moving, and handle empty lists
if (source.Count > 0)
    source.MoveItem(selector, Math.Clamp(targetIndex, 0, source.Count - 1));

Prevention

When it happens

Trigger: Calling list.MoveItem(selector, targetIndex) with targetIndex < 0 or >= list.Count; calling on an empty list; computing targetIndex from a stale count.

Common situations: UI reorder logic that passes an index from a different-sized list; off-by-one (using Count instead of Count-1); calling MoveItem before the list is populated.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/6f7875e34bc0c719. Report an issue: GitHub.