BabylonJS/Babylon.js · error

Output length is not valid

Error message

Output length is not valid

What it means

BufferUtils.CopyFloatData throws "Output length is not valid" when output.length !== totalVertices * size, where count is the exact number of floats the copy will produce. The API writes count floats into the caller-provided Float32Array and validates up front to avoid partial/overflowed writes.

Source

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

 * @param normalized whether the data is normalized
 * @param totalVertices number of vertices in the buffer to take into account
 * @param output the output float array
 */
export function CopyFloatData(
    input: DataArray,
    size: number,
    type: number,
    byteOffset: number,
    byteStride: number,
    normalized: boolean,
    totalVertices: number,
    output: Float32Array
): void {
    const tightlyPackedByteStride = size * GetTypeByteLength(type);
    const count = totalVertices * size;

    if (output.length !== count) {
        throw new Error("Output length is not valid");
    }

    if (type !== Constants.FLOAT || byteStride !== tightlyPackedByteStride) {
        EnumerateFloatValues(input, byteOffset, byteStride, size, type, count, normalized, (values, index) => {
            for (let i = 0; i < size; i++) {
                output[index + i] = values[i];
            }
        });
        return;
    }

    if (input instanceof Array) {
        const offset = byteOffset / 4;
        output.set(input, offset);
    } else if (ArrayBuffer.isView(input)) {
        const offset = input.byteOffset + byteOffset;
        if ((offset & 3) !== 0) {
            Logger.Warn("Float array must be aligned to 4-bytes border");

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Allocate output as new Float32Array(totalVertices * size) using the same totalVertices and size passed to the copy.
  2. Recompute the output buffer from the current vertexBuffer.getTotalVertices() and stride kind instead of caching.
  3. For copyVerticesData, use vertexBuffer.getData()/copyToArray with a correctly sized target: totalVertices * (kind === 'uv' ? 2 : 3).
  4. Trim oversized arrays with new Float32Array(buffer.buffer, 0, totalVertices * size) only when the source array is at least that long.

Example fix

// before
const out = new Float32Array(mesh.getTotalVertices());
CopyFloatData(input, stride, 3, type, totalVertices, normalized, offset, out); // out.length wrong

// after
const size = 3;
const out = new Float32Array(mesh.getTotalVertices() * size);
CopyFloatData(input, stride, size, type, totalVertices, normalized, offset, out);
Defensive patterns

Strategy: validation

Validate before calling

const required = totalVertices * size;
if (!(output instanceof Float32Array) || output.length !== required) {
  output = new Float32Array(required);
}
CopyFloatData(input, byteStride, size, type, totalVertices, normalized, byteOffset, output);

Type guard

function isSizedFloat32Array(x, len) {
  return x instanceof Float32Array && x.length === len;
}

Try / catch

try {
  CopyFloatData(input, stride, size, type, totalVertices, normalized, offset, out);
} catch (e) {
  if (e.message === 'Output length is not valid') {
    out = new Float32Array(totalVertices * size);
    CopyFloatData(input, stride, size, type, totalVertices, normalized, offset, out);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling CopyFloatData (or copyVerticesData reaching it) with a Float32Array allocated as totalVertices * size2, or allocated from a stale vertex count after the mesh's totalVertices changed, or forgetting to multiply by `size` (passing totalVertices only).

Common situations: Extracting positions (size=3) into an array sized for UVs (size=2); reusing a cached buffer after geometry was updated/re-tessellated; slicing Float32Array which can yield lengths not matching the new count.

Related errors


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