BabylonJS/Babylon.js · error

Gaussian splat data byte length (${bytes.byteLength}) is not

Error message

Gaussian splat data byte length (${bytes.byteLength}) is not divisible by ${floatSize} and cannot be reinterpreted as Float32 data.

What it means

_GetSplatDataFloats reinterprets raw splat bytes as a Float32Array; it throws when the byte length is not a multiple of 4 (Float32Array.BYTES_PER_ELEMENT), since such data cannot be viewed as floats. This catches truncated or misaligned splat files early instead of producing garbage geometry.

Source

Thrown at packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.ts:643

     * @returns A Uint8Array covering the exact source byte range.
     * @internal
     */
    protected static _GetSplatDataBytes(data: ArrayBuffer | ArrayBufferView): Uint8Array {
        return ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
    }

    /**
     * Returns a Float32 reinterpretation for retained splat data, copying only when alignment requires it.
     * @param data The retained splat source bytes.
     * @returns A Float32Array over the exact source byte range.
     * @internal
     */
    protected static _GetSplatDataFloats(data: ArrayBuffer | ArrayBufferView): Float32Array {
        const bytes = GaussianSplattingMeshBase._GetSplatDataBytes(data);
        const floatSize = Float32Array.BYTES_PER_ELEMENT;

        if (bytes.byteLength % floatSize !== 0) {
            throw new Error(`Gaussian splat data byte length (${bytes.byteLength}) is not divisible by ${floatSize} and cannot be reinterpreted as Float32 data.`);
        }

        if (bytes.byteOffset % floatSize !== 0) {
            const copy = new Uint8Array(bytes.byteLength);
            copy.set(bytes);
            return new Float32Array(copy.buffer, 0, bytes.byteLength / floatSize);
        }

        return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / floatSize);
    }

    private static _BuildSplatRangeData(
        ranges: Nullable<readonly IGaussianSplattingSplatRange[]>,
        vertexCount: number
    ): { ranges: Nullable<Uint32Array>; count: number; key: string } {
        if (ranges === null) {
            return { ranges: null, count: vertexCount, key: "" };
        }

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Verify the downloaded asset length matches the expected splat count * 32 (or format stride) before passing it to the mesh
  2. Trim or pad the buffer to a multiple of 4 bytes if the trailing bytes are known junk
  3. Re-download the file if truncated

Example fix

// before
mesh.setData(buffer); // buffer.byteLength % 4 !== 0
// after
if (buffer.byteLength % 4 !== 0) {
    buffer = buffer.slice(0, buffer.byteLength - (buffer.byteLength % 4));
}
mesh.setData(buffer);
Defensive patterns

Strategy: validation

Validate before calling

if ((data.byteLength ?? data.buffer.byteLength) % 4 !== 0) {
  throw new RangeError('splat buffer not 4-byte aligned in length');
}

Type guard

function isFloat32Reinterpretable(data: ArrayBuffer | ArrayBufferView): data is ArrayBuffer | ArrayBufferView {
  return (ArrayBuffer.isView(data) ? data.byteLength : data.byteLength) % Float32Array.BYTES_PER_ELEMENT === 0;
}

Try / catch

try {
  mesh.setData(buffer);
} catch (e) {
  if (String(e).includes('not divisible by')) { reDownloadOrTrim(buffer); }
  else throw e;
}

Prevention

When it happens

Trigger: Loading a .splat/.ksplat file that was truncated during download, or passing a byte buffer containing a non-float layout (e.g. half-float or quantized data).

Common situations: Incomplete network fetch of splat assets stored in ArrayBuffer; hand-rolled binary splat writers emitting odd trailing bytes.

Related errors


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