stride3d/stride · error · ArgumentException

The offset of the is not a multiple of 4 bytes

Error message

The offset of the {nameof(VertexElement)} is not a multiple of 4 bytes

What it means

A validation guard in VertexElementValidator.Validate: it fires when a vertex element's computed byte offset is not a multiple of 4. Graphics APIs require 4-byte alignment for vertex element offsets; an element that starts at an unaligned offset would be rejected by the GPU driver, so the validator rejects the vertex declaration early with ArgumentException on the elements parameter.

Solutions

  1. Pad preceding elements so every offset is a multiple of 4
  2. Widen small elements to 4-byte formats (e.g. R16G16 to R32G32)
  3. Recompute offsets sequentially, rounding each up to the next multiple of 4

Example fix

// before
// R16 at offset 0 (2 bytes), R32G32 at offset 2 -> misaligned
// after
// R32 at offset 0 (4 bytes), R32G32 at offset 4
Defensive patterns

Strategy: validation

Validate before calling

bool IsAligned(int offset) => (offset & 3) == 0;

Try / catch

try { VertexElementValidator.Validate(stride, elements); } catch (ArgumentException) { alignOffsetsTo4(); }

Prevention

When it happens

Trigger: Assigning offsets like 2, 6, or 10 in a vertex declaration — typically after adding a small 2-byte element and computing subsequent offsets naively.

Common situations: Packed layouts with 16-bit elements stacked at odd offsets; hand-written offsets copied from struct layouts with packing quirks.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sources/engine/Stride.Graphics/VertexElementValidator.cs:91

        for (int elementIndex = 0; elementIndex < elements.Length; elementIndex++)
        {
            // Compute the offset for this element
            int elementOffset = elements[elementIndex].AlignedByteOffset;
            if (elementOffset == VertexElement.AppendAligned)
            {
                elementOffset = elementIndex == 0 ? 0 : totalOffset + elements[elementIndex - 1].Format.SizeInBytes;
            }
            totalOffset = elementOffset;

            // Validate the element offset
            int typeSize = elements[elementIndex].Format.SizeInBytes;
            if (elementOffset == VertexElement.AppendAligned || (elementOffset + typeSize) > vertexStride)
            {
                throw new ArgumentException($"The {nameof(VertexElement)}'s offset and size makes it extend beyond the vertex stride", nameof(elements));
            }
            if ((elementOffset & 3) != 0)
            {
                throw new ArgumentException($"The offset of the {nameof(VertexElement)} is not a multiple of 4 bytes", nameof(elements));
            }

            // Check for duplicate semantic names and indices
            for (int i = 0; i < elementIndex; i++)
            {
                if (elements[elementIndex].SemanticName.Equals(elements[i].SemanticName) &&
                    elements[elementIndex].SemanticIndex == elements[i].SemanticIndex)
                {
                    throw new ArgumentException($"Duplicate {nameof(VertexElement)}s found with the same semantic name and index", nameof(elements));
                }
            }

            // Check for overlap with existing elements
            for (int i = elementOffset; i < (elementOffset + typeSize); i++)
            {
                // Check for overlap with existing elements
                if (overlapCheckArray[i] >= 0)
                {

View on GitHub (pinned to 96fad776d2)