{"record":{"id":"afaff6a961eee278","repo":"stride3d/stride","slug":"must-be-positive","errorCode":null,"errorMessage":"Must be positive.","messagePattern":"Must be positive\\.","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Particles/Sorters/ArrayPool.cs","lineNumber":21,"sourceCode":"using System;\nusing System.Collections.Generic;\n\nnamespace Stride.Particles.Sorters\n{\n    public class ArrayPool<T> where T : struct\n    {\n        private readonly Dictionary<int, Stack<T[]>> _pool = new Dictionary<int, Stack<T[]>>();\n\n        public static readonly T[] Empty = Array.Empty<T>();\n\n        public virtual void Clear()\n        {\n            _pool.Clear();\n        }\n\n        public virtual T[] Allocate(int size)\n        {\n            if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), \"Must be positive.\");\n\n            if (size == 0) return Empty;\n\n            Stack<T[]> candidates;\n            return _pool.TryGetValue(size, out candidates) && candidates.Count > 0 ? candidates.Pop() : new T[size];\n        }\n\n        public virtual void Free(T[] array)\n        {\n            if (array == null) throw new ArgumentNullException(nameof(array));\n\n            if (array.Length == 0) return;\n\n            Stack<T[]> candidates;\n            if (!_pool.TryGetValue(array.Length, out candidates))\n                _pool.Add(array.Length, candidates = new Stack<T[]>());\n            candidates.Push(array);\n        }","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Particles/Sorters/ArrayPool.cs#L3-L39","documentation":"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.'","triggerScenarios":"Calling Allocate with a negative computed size, e.g. from a particle count or subtraction that went below zero (count - removedCount where removed > count).","commonSituations":"Particle sorter code computing buffer sizes from dynamic particle counts that underflow or are fed invalid data.","solutions":["Clamp the size argument to >= 0 before calling Allocate","Fix the upstream calculation producing the negative size","Use Math.Max(0, size) at the call site"],"exampleFix":"// before\nvar buffer = arrayPool.Allocate(particleCount - removed);\n// after\nvar buffer = arrayPool.Allocate(Math.Max(0, particleCount - removed));","handlingStrategy":"validation","validationCode":"if (size >= 0)\n    var buffer = pool.Allocate(size);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp computed sizes with Math.Max(0, n)","Validate particle counts before computing buffer sizes","Beware of subtractions that can underflow"],"tags":["particles","array-pool","argument-out-of-range"],"backgroundTag":"argument-out-of-range","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}