{"record":{"id":"508b1f86e44312a8","repo":"BabylonJS/Babylon.js","slug":"the-provided-data-is-not-a-valid-ktx2-file","errorCode":null,"errorMessage":"The provided data is not a valid KTX2 file.","messagePattern":"The provided data is not a valid KTX2 file\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts","lineNumber":151,"sourceCode":" * @param options defines optional creation settings\r\n * @returns a promise resolved with the created RawTexture2DArray\r\n */\r\nexport async function CreateTexture2DArrayFromKTX2Async(scene: Scene, data: string | ArrayBufferView, options?: ICreateTexture2DArrayFromKTX2Options): Promise<RawTexture2DArray> {\r\n    let buffer: ArrayBufferView;\r\n    if (typeof data === \"string\") {\r\n        const response = await fetch(data);\r\n        if (!response.ok) {\r\n            throw new Error(`Failed to fetch KTX2 file \"${data}\": ${response.status} ${response.statusText}`);\r\n        }\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","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Materials/Textures/rawTexture2DArray.functions.ts#L133-L169","documentation":"CreateTexture2DArrayFromKTX2Async validates the buffer with KhronosTextureContainer2.IsValid before decoding. If the data does not carry the KTX2 container signature/structure, it throws rather than letting the transcoder fail opaquely.","triggerScenarios":"Passing a buffer that is not KTX2 — a KTX v1 file, a PNG/JPEG mislabeled as .ktx2, a truncated download, or an HTML error page saved as .ktx2.","commonSituations":"Exporting with an old tool producing KTX (v1) instead of KTX2, fetching a 404 page that got cached, or passing a URL string when the server returns non-KTX2 content.","solutions":["Re-export the texture as KTX2 (toktx --t2 or gltf-transform etc.) and confirm the 'KTX 20' magic bytes","Check the served file's first bytes match the KTX2 identifier §0xAB 'KTX' ' ' '2' '0' 'xBB'","Ensure the download is complete (compare Content-Length)","Verify the file is KTX2, not legacy KTX1"],"exampleFix":"// before\nconst data = new Uint8Array(await file.arrayBuffer());\nconst tex = await CreateTexture2DArrayFromKTX2Async(scene, data);\n// after\nconst data = new Uint8Array(await file.arrayBuffer());\nconst isKtx2 = data[0] === 0xab && data[1] === 0x4b && data[2] === 0x54 && data[3] === 0x58 && data[4] === 0x20 && data[5] === 0x32 && data[6] === 0x30 && data[7] === 0xbb;\nif (!isKtx2) throw new Error(`${file.name} is not KTX2`);\nconst tex = await CreateTexture2DArrayFromKTX2Async(scene, data);","handlingStrategy":"validation","validationCode":"function isKtx2Buffer(buf: Uint8Array): boolean {\n  return buf.length >= 12\n    && buf[0] === 0xab && buf[1] === 0x4b && buf[2] === 0x54 && buf[3] === 0x58\n    && buf[4] === 0x20 && buf[5] === 0x32 && buf[6] === 0x30 && buf[7] === 0xbb;\n}\nif (!isKtx2Buffer(data)) throw new Error(\"Not a KTX2 file\");","typeGuard":"function isKtx2(b: Uint8Array): b is Uint8Array {\n  return b.length >= 12 && b[0] === 0xab && b[1] === 0x4b && b[2] === 0x54 && b[3] === 0x58 && b[4] === 0x20 && b[5] === 0x32 && b[6] === 0x30 && b[7] === 0xbb;\n}","tryCatchPattern":"try {\n  const tex = await CreateTexture2DArrayFromKTX2Async(scene, data);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"not a valid KTX2\")) {\n    console.error(\"Asset is not KTX2 — re-export or check the fetched content\");\n  } else throw e;\n}","preventionTips":["Check the KTX2 magic bytes before passing buffers","Confirm fetched content-type is application/octet-stream, not text/html","Standardize exports through toktx or gltf-transform pipelines"],"tags":["ktx2","decoding","validation","asset-format"],"backgroundTag":"invalid-file-format","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}