stride3d/stride · error · ArgumentOutOfRangeException

Must be positive.

Error message

Must be positive.

What it means

ArrayPool.Allocate throws ArgumentOutOfRangeException when 'size' is negative. The pool keys cached arrays by exact size, so negative sizes are meaningless; note that 0 is explicitly allowed and returns the shared empty array, despite the message saying 'Must be positive.'

Solutions

  1. Clamp the size argument to >= 0 before calling Allocate
  2. Fix the upstream calculation producing the negative size
  3. Use Math.Max(0, size) at the call site

Example fix

// before
var buffer = arrayPool.Allocate(particleCount - removed);
// after
var buffer = arrayPool.Allocate(Math.Max(0, particleCount - removed));
Defensive patterns

Strategy: validation

Validate before calling

if (size >= 0)
    var buffer = pool.Allocate(size);

Prevention

When it happens

Trigger: Calling Allocate with a negative computed size, e.g. from a particle count or subtraction that went below zero (count - removedCount where removed > count).

Common situations: Particle sorter code computing buffer sizes from dynamic particle counts that underflow or are fed invalid data.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/afaff6a961eee278. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Particles/Sorters/ArrayPool.cs:21

using System;
using System.Collections.Generic;

namespace Stride.Particles.Sorters
{
    public class ArrayPool<T> where T : struct
    {
        private readonly Dictionary<int, Stack<T[]>> _pool = new Dictionary<int, Stack<T[]>>();

        public static readonly T[] Empty = Array.Empty<T>();

        public virtual void Clear()
        {
            _pool.Clear();
        }

        public virtual T[] Allocate(int size)
        {
            if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), "Must be positive.");

            if (size == 0) return Empty;

            Stack<T[]> candidates;
            return _pool.TryGetValue(size, out candidates) && candidates.Count > 0 ? candidates.Pop() : new T[size];
        }

        public virtual void Free(T[] array)
        {
            if (array == null) throw new ArgumentNullException(nameof(array));

            if (array.Length == 0) return;

            Stack<T[]> candidates;
            if (!_pool.TryGetValue(array.Length, out candidates))
                _pool.Add(array.Length, candidates = new Stack<T[]>());
            candidates.Push(array);
        }

View on GitHub (pinned to 96fad776d2)