{"record":{"id":"d1b173d31e5dc580","repo":"BabylonJS/Babylon.js","slug":"failed-to-decode-the-ktx2-file-layer-mipmap-lay-d1b173","errorCode":null,"errorMessage":"Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level holds ${mipmap.data.byteLength} bytes but ${expectedLayerByteLength} were expected.","messagePattern":"Failed to decode the KTX2 file: layer (.+?) of the base mip level holds (.+?) bytes but (.+?) were expected\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts","lineNumber":188,"sourceCode":"    }\r\n\r\n    // Every layer of a level shares the level's dimensions, and forceRGBA above means each one must be exactly\r\n    // width * height * 4 bytes. RawTexture2DArray takes all the layers as one flat buffer and cannot detect a\r\n    // short or mismatched layer, so validate here rather than uploading malformed data.\r\n    const { width, height } = baseLevel[0];\r\n    const expectedLayerByteLength = width * height * 4;\r\n\r\n    for (const mipmap of baseLevel) {\r\n        if (!mipmap.data) {\r\n            throw new Error(`Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level is empty.`);\r\n        }\r\n        if (mipmap.width !== width || mipmap.height !== height) {\r\n            throw new Error(\r\n                `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}.`\r\n            );\r\n        }\r\n        if (mipmap.data.byteLength !== expectedLayerByteLength) {\r\n            throw new Error(\r\n                `Failed to decode the KTX2 file: layer ${mipmap.layerIndex} of the base mip level holds ${mipmap.data.byteLength} bytes but ${expectedLayerByteLength} were expected.`\r\n            );\r\n        }\r\n    }\r\n\r\n    // A 2D array texture is uploaded as a single buffer holding every layer back to back.\r\n    const textureData = new Uint8Array(expectedLayerByteLength * layerCount);\r\n    let byteOffset = 0;\r\n    for (const mipmap of baseLevel) {\r\n        textureData.set(mipmap.data!, byteOffset);\r\n        byteOffset += mipmap.data!.byteLength;\r\n    }\r\n\r\n    return new RawTexture2DArray(\r\n        textureData,\r\n        width,\r\n        height,\r\n        layerCount,\r","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts#L170-L206","documentation":"Each layer of the base mip level must contain exactly width*height*4 bytes (RGBA8 uncompressed). This error is thrown when a decoded layer's byte length does not match the expected size, indicating the decoder produced data in an unexpected format or size.","triggerScenarios":"Calling CreateTexture2DArrayFromKTX2Async where the transcoded base-level layer data byte length differs from width*height*4 — e.g. mismatched transcoder output format, partially decoded layer, or corrupt file.","commonSituations":"Version mismatch between the Babylon.js core and the loaded basis/KTX2 transcoder WASM, misconfigured transcoder formats (e.g. requesting a compressed output the array path doesn't expect), corrupted assets.","solutions":["Ensure the KTX2/basis transcoder WASM files match your @babylonjs/core version (same release line).","Re-export the KTX2 file without unusual supercompression or with a supported output format.","Verify the file is not truncated or corrupted (checksum against the original export).","Update @babylonjs/core to the latest patch where this decode path may be fixed."],"exampleFix":"// before: mismatched transcoder assets\nengine._loadFile(\"old_basis_encoder/basis_transcoder.js\");\n// after: load the transcoder matching the core version\nawait scene.getEngine()._ktx2Decoder.initializeAsync(matchingTranscoderUrl);","handlingStrategy":"validation","validationCode":"// Ensure layer byte length matches RGBA8 expectation after decode\nconst expected = width * height * 4;\nif (layerData.byteLength !== expected) {\n  throw new Error(`Layer holds ${layerData.byteLength} bytes, expected ${expected}`);\n}","typeGuard":"function isExpectedLayerBytes(m: { data: ArrayBuffer | null; width: number; height: number }): boolean {\n  return m.data != null && m.data.byteLength === m.width * m.height * 4;\n}","tryCatchPattern":"try {\n  const tex = await CreateTexture2DArrayFromKTX2Async(url, scene);\n} catch (e) {\n  if (String(e.message).includes(\"were expected\")) {\n    console.error(\"Transcoder output format mismatch — check transcoder WASM version.\");\n  }\n}","preventionTips":["Load the KTX2 transcoder from the same Babylon.js release as your core package.","Pin transcoder asset versions in deployment (no floating 'latest').","Add asset checksums to CI to catch corruption early."],"tags":["ktx2","texture-array","byte-size","transcoder"],"backgroundTag":"ktx2-layer-size-mismatch","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}