dotnet/reactive · error · ArgumentOutOfRangeException

Specified argument was out of the range of valid values…

Error message

Specified argument was out of the range of valid values. (Parameter 'arrayIndex')

What it means

CompositeDisposable.CopyTo throws ArgumentOutOfRangeException when arrayIndex is negative or greater than or equal to the array length. The starting index must point inside the destination array; this first bounds check happens before the lock and before accounting for the number of elements to copy.

Solutions

  1. Validate the index first: if (arrayIndex < 0 || arrayIndex >= array.Length) throw/skip.
  2. For an empty composite, use CopyTo(arr, 0) with a non-empty array or use LINQ ToArray().
  3. Clamp arrayIndex to Math.Min(index, array.Length - 1) when the copy is optional.

Example fix

// before
composite.CopyTo(buffer, offset); // offset can equal buffer.Length
// after
if (offset >= 0 && offset < buffer.Length)
{
    composite.CopyTo(buffer, offset);
}
Defensive patterns

Strategy: validation

Validate before calling

if (arrayIndex < 0 || arrayIndex >= array.Length) throw new ArgumentException("arrayIndex outside destination array");
composite.CopyTo(array, arrayIndex);

Type guard

bool IsValidIndex<T>(T[] a, int i) => a != null && i >= 0 && i < a.Length;

Try / catch

try { composite.CopyTo(array, index); } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "arrayIndex") { /* clamp or log */ }

Prevention

When it happens

Trigger: Calling composite.CopyTo(arr, -1) or composite.CopyTo(new IDisposable[3], 3); also when arrayIndex is computed from a variable that drifted (e.g. an offset from a previous partial copy).

Common situations: Manual paging/copy loops that advance an offset without revalidating it against the array length; off-by-one errors where arrayIndex == array.Length was assumed valid as an exclusive end.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/858c33b3383dc21c. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Disposables/CompositeDisposable.cs:432

        }

        /// <summary>
        /// Copies the disposables contained in the <see cref="CompositeDisposable"/> to an array, starting at a particular array index.
        /// </summary>
        /// <param name="array">Array to copy the contained disposables to.</param>
        /// <param name="arrayIndex">Target index at which to copy the first disposable of the group.</param>
        /// <exception cref="ArgumentNullException"><paramref name="array"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than zero. -or - <paramref name="arrayIndex"/> is larger than or equal to the array length.</exception>
        public void CopyTo(IDisposable[] array, int arrayIndex)
        {
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }

            if (arrayIndex < 0 || arrayIndex >= array.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(arrayIndex));
            }

            lock (_gate)
            {
                // disposed composites are always empty
                if (_disposed)
                {
                    return;
                }

                if (arrayIndex + _count > array.Length)
                {
                    // there is not enough space beyond arrayIndex 
                    // to accommodate all _count disposables in this composite
                    throw new ArgumentOutOfRangeException(nameof(arrayIndex));
                }
                
                var i = arrayIndex;

View on GitHub (pinned to 94b5d5ab91)