BabylonJS/Babylon.js · error · Error

Failed to decode the KTX2 file: layer ${mipmap.layerIndex} o

Error message

Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level is empty.

What it means

When creating a 2D texture array from a KTX2 file, Babylon.js decodes the container and validates every layer of the base mip level before uploading it to the GPU. This error means one layer's data buffer is missing/null, so the file is malformed or the transcoder failed to produce data for that layer.

Source

Thrown at packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts:180

    const layerCount = Math.max(decodedData.layerCount ?? 1, 1);
    // mipmaps are ordered level by level, and within a level layer by layer, so the base level occupies
    // the first layerCount entries and its layers are already in ascending order.
    const baseLevel = decodedData.mipmaps.slice(0, layerCount);

    if (baseLevel.length !== layerCount) {
        throw new Error(`Failed to decode the KTX2 file: expected ${layerCount} layers for the base mip level but got ${baseLevel.length}.`);
    }

    // Every layer of a level shares the level's dimensions, and forceRGBA above means each one must be exactly
    // width * height * 4 bytes. RawTexture2DArray takes all the layers as one flat buffer and cannot detect a
    // short or mismatched layer, so validate here rather than uploading malformed data.
    const { width, height } = baseLevel[0];
    const expectedLayerByteLength = width * height * 4;

    for (const mipmap of baseLevel) {
        if (!mipmap.data) {
            throw new Error(`Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level is empty.`);
        }
        if (mipmap.width !== width || mipmap.height !== height) {
            throw new Error(
                `Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level is ${mipmap.width}x${mipmap.height} but layer 0 is ${width}x${height}.`
            );
        }
        if (mipmap.data.byteLength !== expectedLayerByteLength) {
            throw new Error(
                `Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level holds ${mipmap.data.byteLength} bytes but ${expectedLayerByteLength} were expected.`
            );
        }
    }

    // A 2D array texture is uploaded as a single buffer holding every layer back to back.
    const textureData = new Uint8Array(expectedLayerByteLength * layerCount);
    let byteOffset = 0;
    for (const mipmap of baseLevel) {
        textureData.set(mipmap.data!, byteOffset);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Re-export or re-encode the KTX2 file with a reliable tool (e.g. toktx / gltf-transform) and verify all array layers are populated.
  2. Check the KTX2 file is fully downloaded and not truncated (compare Content-Length / byte size).
  3. Update @babylonjs/core and the KTX2 transcoder (basis transcoder) to matching versions so all layers decode.
  4. If the file is valid, report the failing encoder settings to the Babylon.js team with the file.

Example fix

// before: loading a suspect asset directly
const tex = await CreateTexture2DArrayFromKTX2Async(url, scene);
// after: pre-validate the asset (e.g. with a tool) and catch decode errors
try {
  const tex = await CreateTexture2DArrayFromKTX2Async(validatedUrl, scene);
} catch (e) {
  console.error("KTX2 asset invalid, re-export it:", e);
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate a KTX2 file before loading: ensure non-zero size and trusted source
const res = await fetch(ktx2Url);
if (!res.ok) throw new Error("asset missing");
const buf = await res.arrayBuffer();
if (buf.byteLength < 100) throw new Error("KTX2 file truncated");
// then load from the validated buffer via the supported API

Type guard

function hasLayerData(m: { layerIndex: number; data: ArrayBuffer | null | undefined }): m is { layerIndex: number; data: ArrayBuffer } {
  return m.data != null && m.data.byteLength > 0;
}

Try / catch

try {
  const tex = await CreateTexture2DArrayFromKTX2Async(url, scene);
} catch (e) {
  console.error("Invalid KTX2 asset, replacing with fallback:", e);
  texture = fallbackTexture;
}

Prevention

When it happens

Trigger: Calling CreateTexture2DArrayFromKTX2Async with a KTX2 file whose base mip level contains an array layer with no data (mipmap.data is null/undefined).

Common situations: Corrupt or truncated KTX2 downloads, KTX2 files saved by tooling that leaves layers empty, unsupported supercompression/transcoder paths that skip layers, hand-edited KTX2 containers.

Understand the failure class

Related errors


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