BabylonJS/Babylon.js · error

Byte stride cannot be smaller than the component's byte size

Error message

Byte stride cannot be smaller than the component's byte size.

What it means

BufferUtils.GetTypedArrayData throws when the caller-supplied byteStride is smaller than size * typeByteLength (the bytes one tightly-packed row of `size` components actually needs). A stride smaller than a row would make rows overlap, which typed array views over the buffer cannot represent, so the library refuses instead of producing corrupt data.

Source

Thrown at packages/dev/core/src/Buffers/bufferUtils.ts:361

    // Handle ArrayBuffer and ArrayBufferView
    let buffer: ArrayBufferLike;
    let adjustedByteOffset = byteOffset;

    if (ArrayBuffer.isView(data)) {
        buffer = data.buffer;
        adjustedByteOffset += data.byteOffset;
    } else {
        buffer = data;
    }

    const lastByteOffset = adjustedByteOffset + (totalVertices - 1) * byteStride + size * typeByteLength;
    if (lastByteOffset > buffer.byteLength) {
        throw new Error("Last accessed byte is out of bounds.");
    }

    const tightlyPackedByteStride = size * typeByteLength;
    if (byteStride < tightlyPackedByteStride) {
        throw new Error("Byte stride cannot be smaller than the component's byte size.");
    }
    if (byteStride !== tightlyPackedByteStride) {
        const copy = new constructor(count);
        const src = new Uint8Array(buffer, adjustedByteOffset);
        const dst = new Uint8Array(copy.buffer);
        const rowBytes = size * typeByteLength;
        for (let v = 0, s = 0, d = 0; v < totalVertices; v++, s += byteStride, d += rowBytes) {
            dst.set(src.subarray(s, s + rowBytes), d);
        }
        return copy;
    }

    if (typeByteLength !== 1 && (adjustedByteOffset & (typeByteLength - 1)) !== 0) {
        Logger.Warn("Array must be aligned to border of element size. Data will be copied.");
        forceCopy = true;
    }

    if (forceCopy) {

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Set byteStride >= size * typeByteLength; if data is tightly packed pass exactly size * typeByteLength (or the same value used to create the buffer).
  2. Recompute stride from the actual type: GetTypeByteLength(type) * size.
  3. If reading an interleaved buffer, use the stride of the whole vertex layout, which must be larger than the per-attribute stride.
  4. Pass undefined/null byteStride where the API allows so it defaults to tightly packed.

Example fix

// before
const data = vertexBuffer.typedData; // internally byteStride = 8, size = 4, FLOAT (16 needed)

// after
const byteStride = 4 * 4; // size * GetTypeByteLength(Constants.FLOAT)
const data = vertexBuffer.getTypedData(0, 1 /* vertices */, byteStride); // or fix the layout stride to >= 16
Defensive patterns

Strategy: validation

Validate before calling

const typeByteLength = vertexBuffer.getTypeByteLength?.() ?? 4; // FLOAT
const minStride = size * typeByteLength;
if (byteStride !== undefined && byteStride < minStride) {
  throw new RangeError(`byteStride ${byteStride} < required ${minStride}`);
}
const data = vertexBuffer.getTypedData(vertexStart, vertexCount, Math.max(byteStride, minStride));

Type guard

function hasValidStride(size, typeByteLength, byteStride) {
  return byteStride === undefined || byteStride >= size * typeByteLength;
}

Try / catch

let data;
try {
  data = vertexBuffer.typedData;
} catch (e) {
  if (/Byte stride/.test(e.message)) {
    data = vertexBuffer.getTypedData(0, vertexBuffer.getTotalVertices(), size * 4);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling GetTypedArrayData (directly or via VertexBuffer.data/typedData/typedArray) with a byteStride value manually computed as e.g. (size-1)*4 or copied from a stride meant for a different attribute/type; passing a stride of 0 while size > 1 or a mismatched typeByteLength (e.g. reading FLOAT data with a DOUBLE-sized type constant).

Common situations: Importing glTF/obj data with hand-rolled stride math; reading interleaved vertex buffers where the stride was computed for one attribute but reused for another; changing `size` (components per vertex) without updating byteStride.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/c79cb4954c9bef41. Report an issue: GitHub.