stride3d/stride · error · InvalidOperationException

Invalid state for

Error message

Invalid state for {resource.CommandListFenceValue}

What it means

After resolving the updating command list, mapping a Staging resource requires a recorded CommandListFenceValue to wait on. If it is null, Stride's internal state is inconsistent (no fence value was recorded for the resource) and it throws InvalidOperationException including the null value in the message.

Solutions

  1. Ensure staging resources are only updated through standard APIs (SetData/GetData) so fence values are recorded
  2. Check for Stride version regressions around staging mapping and update to a fixed release
  3. Avoid manually resetting/clearing resource.UpdatingCommandList or CommandListFenceValue state
Defensive patterns

Strategy: try-catch

Try / catch

try { cmdList.MapSubResource(staging, 0, MapMode.Read); } catch (InvalidOperationException ex) when (ex.Message.StartsWith("Invalid state for")) { /* treat as engine bug: recreate staging resource or file an issue */ }

Prevention

When it happens

Trigger: Mapping a Staging resource whose CommandListFenceValue was never set because the update path did not record a fence value (internal state bug or resource updated before the device fence was established).

Common situations: Engine-internal invariant breakage when mixing low-level resource updates with custom command list management; hitting the message with 'Invalid state for ' (empty) because the value is null.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/Vulkan/CommandList.Vulkan.cs:1602

                    // updated in a previous command list which hasn't be finished yet
                    || (resource.CommandListFenceValue is not null && !GraphicsDevice.CommandListFence.IsFenceCompleteInternal(resource.CommandListFenceValue.Value)))
                {
                    // User told us not to wait, return right away
                    if (doNotWait)
                    {
                        return new MappedResource(resource, subResourceIndex, dataBox: default);
                    }

                    if (resource.UpdatingCommandList == this)
                        // Need to flush? (check if part of current command list)
                        // resource.CommandListFenceValue should be set after
                        FlushInternal(false);
                    else if (resource.UpdatingCommandList is not null)
                        // Another command list updated this resource, but it's not been submitted (otherwise it would be stored in resource.CommandListFenceValue
                        throw new InvalidOperationException("CommandList updating the staging resource has not been submitted");

                    if (resource.CommandListFenceValue is null)
                        throw new InvalidOperationException($"Invalid state for {resource.CommandListFenceValue}");

                    GraphicsDevice.CommandListFence.WaitForFenceCPUInternal(resource.CommandListFenceValue.Value);

                    // We're now up to date, remove command list fence value (if any)
                    resource.CommandListFenceValue = null;
                }
            }

            // Also make sure all copy queues are done
            // (important for all cases, since it uploads initial data and also set resource barrier)
            if (resource.CopyFenceValue.HasValue)
            {
                GraphicsDevice.CopyFence.WaitForFenceCPUInternal(resource.CopyFenceValue.Value);
                resource.CopyFenceValue = null;
            }

            void* mappedMemory;
            GraphicsDevice.NativeDeviceApi.vkMapMemory(GraphicsDevice.NativeDevice, resource.NativeMemory, (ulong) offsetInBytes, (ulong) lengthInBytes, VkMemoryMapFlags.None, &mappedMemory);

View on GitHub (pinned to 96fad776d2)