stride3d/stride · error · ArgumentException

Size of T is not a multiple of totalSize

Error message

Size of T is not a multiple of totalSize {0}

What it means

GetVertexBufferData copies the mesh's raw vertex bytes into a T[] using unsafe pointers. The total byte size must be an exact multiple of sizeof(T); if the vertex data size is not divisible by T's size, the copy would lose or misinterpret bytes, so an ArgumentException with the total size is thrown.

Solutions

  1. Use a T whose size exactly matches the vertex declaration stride (declare struct fields to mirror the declaration, add missing fields/padding)
  2. Fix the vertex declaration so its stride matches sizeof(T) times the layout
  3. Read into byte[] and reinterpret/marshal manually instead of a fixed T

Example fix

// before
var data = mesh.GetVertexBufferData<MyPackedVertex>(); // stride 24, sizeof(MyPackedVertex)=28
// after
[StructLayout(LayoutKind.Sequential)]
struct MyPackedVertex { public Vector3 Pos; public Vector3 Normal; } // 24 bytes, matches stride
var data = mesh.GetVertexBufferData<MyPackedVertex>();
Defensive patterns

Strategy: validation

Validate before calling

int expectedSize = mesh.VertexBuffers[0].Declaration.VertexStride;
int count = mesh.VertexBuffers[0].Count;
int outputSize = expectedSize * count;
if (outputSize % Unsafe.SizeOf<T>() != 0)
    throw new InvalidOperationException($"T (sizeof {Unsafe.SizeOf<T>()}) does not divide {outputSize} bytes");

Type guard

static bool CanReadAs<T>(MeshDraw mesh) where T : unmanaged =>
    (mesh.VertexBuffers[0].Declaration.VertexStride * mesh.VertexBuffers[0].Count)
    % Unsafe.SizeOf<T>() == 0;

Try / catch

try { var data = mesh.GetVertexBufferData<T>(); }
catch (ArgumentException ex) when (ex.Message.StartsWith("Size of T is not a multiple")) {
    // fix the struct layout / declaration stride, or read as byte[] and marshal
}

Prevention

When it happens

Trigger: Calling GetVertexBufferData<T> where expectedSize * count is not a multiple of Unsafe.SizeOf<T>() — e.g. reading vertices as a struct whose stride differs from the vertex declaration stride, or T larger than the per-vertex size.

Common situations: Mismatch between the vertex declaration stride and the C# struct used for T; reading padding-containing structs against packed vertex data; querying float4 data into float3 structs or vice versa.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Rendering/Extensions/VertexExtensions.cs:35

        /// </summary>
        /// <param name="meshData">The mesh data.</param>
        /// <param name="vertexElementToExtract">The declaration to extract (e.g. "POSITION0"...etc.) </param>
        [Obsolete($"Use {nameof(VertexBufferHelper)} instead")]
        public static unsafe T[] GetVertexBufferData<T>(this MeshDraw meshData, params string[] vertexElementToExtract) where T : unmanaged
        {
            var declaration = meshData.VertexBuffers[0].Declaration;

            var offsets = declaration.EnumerateWithOffsets().Where(vertexElementOffset => vertexElementToExtract.Contains(vertexElementOffset.VertexElement.SemanticAsText)).ToList();

            int expectedSize = offsets.Sum(vertexElementWithOffset => vertexElementWithOffset.Size);

            var count = meshData.VertexBuffers[0].Count;

            int outputSize = expectedSize * count;

            int checkSize = (int)(outputSize / Unsafe.SizeOf<T>()) * Unsafe.SizeOf<T>();
            if (checkSize != outputSize)
                throw new ArgumentException(string.Format("Size of T is not a multiple of totalSize {0}", outputSize));

            var output = new T[outputSize / Unsafe.SizeOf<T>()];

            fixed (T* ptrOutputT = output)
            fixed (byte* ptrInputStart = meshData.VertexBuffers[0].Buffer.GetSerializationData().Content) {
                var ptrOutput = (byte*)ptrOutputT;
                var ptrInput = ptrInputStart;
                for (int i = 0; i < count; i++)
                {
                    foreach (var vertexElementWithOffset in offsets)
                    {
                        MemoryUtilities.CopyWithAlignmentFallback(ptrOutput, ptrInput + vertexElementWithOffset.Offset, (uint)vertexElementWithOffset.Size);
                        ptrOutput += vertexElementWithOffset.Size;
                    }
                    ptrInput += declaration.VertexStride;
                }
            }

View on GitHub (pinned to 96fad776d2)