stride3d/stride · error · ArgumentOutOfRangeException

count

Error message

count

What it means

Argument-null validation on the count parameter of GraphicsDevice.ExecuteCommandLists: the number of command lists to execute is null/negative. The unsafe execution loop iterates count times over the commandLists array, so a bad count would read out of bounds; the method rejects it up front, as declared in its documentation.

Solutions

  1. Pass count equal to (or less than) the actual array length
  2. Derive count from the array (commandLists.Length) instead of a separate variable
  3. Assert the invariant count <= commandLists.Length in your render loop before submitting

Example fix

// before
GraphicsDevice.ExecuteCommandLists(5, lists); // lists.Length == 3
// after
GraphicsDevice.ExecuteCommandLists(lists.Length, lists);
Defensive patterns

Strategy: validation

Validate before calling

count = Math.Min(count, commandLists?.Length ?? 0);
GraphicsDevice.ExecuteCommandLists(count, commandLists);

Try / catch

try
{
    GraphicsDevice.ExecuteCommandLists(count, commandLists);
}
catch (ArgumentOutOfRangeException ex)
{
    Logger.Error(ex, $"count={count} exceeds commandLists.Length={commandLists.Length}");
}

Prevention

When it happens

Trigger: Calling ExecuteCommandLists with count > commandLists.Length, e.g. after filtering the array without updating the count.

Common situations: Code that trims or reuses a shared array but keeps a stale count; off-by-one errors when batching command lists; copy-pasted submit code where the count came from a different collection.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/GraphicsDevice.Vulkan.cs:306

        /// <summary>
        /// Executes a deferred command list.
        /// </summary>
        /// <param name="commandList">The deferred command list.</param>
        public void ExecuteCommandList(CompiledCommandList commandList)
        {
            ExecuteCommandListInternal(commandList);
        }

        /// <summary>
        /// Executes multiple deferred command lists.
        /// </summary>
        /// <param name="count">Number of command lists to execute.</param>
        /// <param name="commandLists">The deferred command lists.</param>
        public unsafe void ExecuteCommandLists(int count, CompiledCommandList[] commandLists)
        {
            if (commandLists == null) throw new ArgumentNullException(nameof(commandLists));
            if (count > commandLists.Length) throw new ArgumentOutOfRangeException(nameof(count));

            var commandBufferInfos = stackalloc VkCommandBufferSubmitInfo[count];
            for (int i = 0; i < count; i++)
                commandBufferInfos[i] = CommandBufferSubmit(commandLists[i].NativeCommandBuffer);

            ulong nextCommandListFenceValue;
            lock (QueueLock)
            {
                var commandListFenceValue = CommandListFence.NextFenceValue++;
                nextCommandListFenceValue = commandListFenceValue + 1;
                // Make sure all copies are done as well
                var copyFenceValue = CopyFence.NextFenceValue;

                var waitInfos = stackalloc VkSemaphoreSubmitInfo[]
                {
                    SemaphoreSubmit(CommandListFence.Semaphore, commandListFenceValue),
                    SemaphoreSubmit(CopyFence.Semaphore, copyFenceValue),
                };

View on GitHub (pinned to 96fad776d2)