{"record":{"id":"2505650dc75790c4","repo":"BabylonJS/Babylon.js","slug":"failed-to-decode-the-ktx2-file-decodeddata-erro","errorCode":null,"errorMessage":"Failed to decode the KTX2 file. ${decodedData.errors}","messagePattern":"Failed to decode the KTX2 file\\. (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts","lineNumber":160,"sourceCode":"        }\r\n        buffer = new Uint8Array(await response.arrayBuffer());\r\n    } else {\r\n        buffer = data;\r\n    }\r\n\r\n    const { KhronosTextureContainer2 } = await import(\"../../Misc/khronosTextureContainer2\");\r\n\r\n    if (!KhronosTextureContainer2.IsValid(buffer)) {\r\n        throw new Error(\"The provided data is not a valid KTX2 file.\");\r\n    }\r\n\r\n    const container = new KhronosTextureContainer2(scene.getEngine());\r\n\r\n    // forceRGBA: the transcoded compressed formats cannot be uploaded to an array texture yet (see above).\r\n    const decodedData = await container._decodeAsync(buffer, { forceRGBA: true });\r\n\r\n    if (decodedData.errors) {\r\n        throw new Error(\"Failed to decode the KTX2 file. \" + decodedData.errors);\r\n    }\r\n\r\n    const layerCount = Math.max(decodedData.layerCount ?? 1, 1);\r\n    // mipmaps are ordered level by level, and within a level layer by layer, so the base level occupies\r\n    // the first layerCount entries and its layers are already in ascending order.\r\n    const baseLevel = decodedData.mipmaps.slice(0, layerCount);\r\n\r\n    if (baseLevel.length !== layerCount) {\r\n        throw new Error(`Failed to decode the KTX2 file: expected ${layerCount} layers for the base mip level but got ${baseLevel.length}.`);\r\n    }\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","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts#L142-L178","documentation":"After validation, the KTX2 buffer is transcoded with _decodeAsync using forceRGBA. If the transcoder reports errors (unsupported format, corrupted data, missing transcoder wasm/JS), the library throws with the error list appended to the message.","triggerScenarios":"KTX2 files using transcode targets or supercompression schemes the decoder cannot handle, corrupted buffers, or the KhronosTextureContainer2 wasm/JS runtime files not being served so decoding fails.","commonSituations":"Not configuring the KTX2 transcoder URLs (KhronosTextureContainer2.URLConfig), server missing .wasm/.js MIME types, or an exotic basis format from the exporter.","solutions":["Set KhronosTextureContainer2.URLConfig to serve the KTX2 transcoder .wasm/.js/.jsm files from your CDN","Re-export the KTX2 with a standard UASTC/ETC1S transcode setting","Inspect decodedData.errors in the message for the specific codec problem","Read the file with the official ktx tools to confirm integrity"],"exampleFix":"// before\nconst tex = await CreateTexture2DArrayFromKTX2Async(scene, buffer);\n// after\nimport { KhronosTextureContainer2 } from \"@babylonjs/core/Misc/khronosTextureContainer2\";\nKhronosTextureContainer2.URLConfig = { jsDecoder: \"/cdn/ktx2/ktx2Transcoder.js\", wasmDecoder: \"/cdn/ktx2/ktx2Transcoder.wasm\" };\nconst tex = await CreateTexture2DArrayFromKTX2Async(scene, buffer);","handlingStrategy":"try-catch","validationCode":"import { KhronosTextureContainer2 } from \"@babylonjs/core/Misc/khronosTextureContainer2\";\nKhronosTextureContainer2.URLConfig = {\n  jsDecoder: \"/cdn/ktx2/ktx2Transcoder.js\",\n  jsModuleDecoder: \"/cdn/ktx2/ktx2Transcoder.jsm\",\n  wasmDecoder: \"/cdn/ktx2/ktx2Transcoder.wasm\"\n};","typeGuard":null,"tryCatchPattern":"try {\n  const tex = await CreateTexture2DArrayFromKTX2Async(scene, buffer);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Failed to decode the KTX2 file\")) {\n    console.error(\"KTX2 transcode failed:\", e.message);\n    return createRGBAArrayTextureFallback(scene, buffer);\n  }\n  throw e;\n}","preventionTips":["Configure KhronosTextureContainer2.URLConfig to hosted transcoder files","Ensure .wasm/.js MIME types on the CDN","Use standard UASTC/ETC1S export settings","Test-load one KTX2 asset at startup to catch transcoder setup issues"],"tags":["ktx2","decoding","transcoder"],"backgroundTag":"asset-decode-failed","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}