stride3d/stride · error · ArgumentOutOfRangeException

Type vertex can't be a byte

Error message

Type vertex can't be a byte

What it means

The generic overload GenerateTangentBinormal<T> only supports unmanaged vertex structs with a meaningful layout; T == byte is rejected with ArgumentOutOfRangeException because byte gives stride 1 and no way to interpret vertex semantics.

Solutions

  1. Define or use a proper vertex struct (e.g. PositionNormalTexture) and cast/copy the bytes into T[] before calling.
  2. Use the non-generic overload taking IntPtr, vertexCount, and vertexStride instead of the generic byte invocation.
  3. Use VertexHelper.Convert / BytesToTypedArray-style conversion utilities to reinterpret the data.

Example fix

// before
byte[] raw = mesh.GetVertexBytes();
var result = VertexHelper.GenerateTangentBinormal(declaration, raw, indexBuffer);

// after
var verts = new PositionNormalTexture[raw.Length / declaration.VertexStride];
Buffer.BlockCopy(raw, 0, verts, 0, raw.Length);
var result = VertexHelper.GenerateTangentBinormal(declaration, verts, indexBuffer);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof(T) == typeof(byte))
    throw new InvalidOperationException("Convert raw bytes to a vertex struct before calling GenerateTangentBinormal");

Type guard

bool IsSupportedVertexType<T>() where T : unmanaged => typeof(T) != typeof(byte);

Try / catch

try { var result = VertexHelper.GenerateTangentBinormal(decl, data, indices); }
catch (ArgumentOutOfRangeException ex) { log.Error("Unsupported vertex element type; use a real vertex struct", ex); }

Prevention

When it happens

Trigger: Calling VertexHelper.GenerateTangentBinormal<byte>(declaration, rawBytes, indexBuffer) — typically when holding the vertex data as a raw byte array copied from a buffer.

Common situations: Reading vertex data back from a GPU buffer into byte[] then passing it straight into the generic API; serializing code paths that treat vertex data as opaque bytes.

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/068163659f472e50. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Graphics/VertexHelper.cs:209

            var allVertexElements = new List<VertexElement>(vertexDeclaration.VertexElements);
            allVertexElements.AddRange(newVertexElements);

            return new VertexTransformResult(new VertexDeclaration(allVertexElements.ToArray()), newVertexBuffer);
        }

        /// <summary>
        /// Generates the tangent binormal for an existing vertex buffer.
        /// </summary>
        /// <param name="vertexDeclaration">The vertex declaration.</param>
        /// <param name="vertexBufferData">The vertex buffer data.</param>
        /// <param name="indexBuffer">The index buffer.</param>
        /// <returns>A new vertex buffer with its new layout.</returns>
        public static unsafe VertexTransformResult GenerateTangentBinormal<T>(VertexDeclaration vertexDeclaration, T[] vertexBufferData, int[] indexBuffer) where T : unmanaged
        {
            if (vertexDeclaration == null) throw new ArgumentNullException("vertexDeclaration");
            if (vertexBufferData == null) throw new ArgumentNullException("vertexBufferData");
            if (typeof(T) == typeof(byte)) throw new ArgumentOutOfRangeException("T", "Type vertex can't be a byte");

            var vertexStride = Unsafe.SizeOf<T>();
            fixed (void* vertexBufferPtr = vertexBufferData)
            fixed (void* indexBufferPtr = indexBuffer)
            {
                return GenerateTangentBinormal(vertexDeclaration, (nint)vertexBufferPtr, vertexBufferData.Length, 0, vertexStride, (nint)indexBufferPtr, true, indexBuffer?.Length ?? 0);
            }
        }

        /// <summary>
        /// Generate Tangent BiNormal from an existing vertex/index buffer.
        /// </summary>
        /// <param name="vertexDeclaration">The vertex declaration of the vertex buffer passed by parameters.</param>
        /// <param name="vertexBufferData">The vertex buffer.</param>
        /// <param name="vertexCount">The vertex count.</param>
        /// <param name="vertexOffset">The vertex offset.</param>
        /// <param name="vertexStride">The vertex stride. If 0, It takes the stride from the vertex declaration</param>
        /// <param name="indexData">The index data.</param>

View on GitHub (pinned to 96fad776d2)