BabylonJS/Babylon.js · error · Error

Failed to fetch KTX2 file "${data}": ${response.status} ${re

Error message

Failed to fetch KTX2 file "${data}": ${response.status} ${response.statusText}

What it means

CreateTexture2DArrayFromKTX2Async accepts either an ArrayBufferView or a URL string. When a string is passed it fetches the file and throws a descriptive Error if the HTTP response is not ok, embedding status code and status text.

Source

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

 *
 * The data is transcoded to uncompressed RGBA. Keeping the texture in its transcoded compressed form would
 * require compressedTexImage3D support in the engine raw texture path, which does not exist yet, so the
 * GPU cost here matches a plain RGBA array texture.
 *
 * Only the base mip level stored in the file is uploaded; when generateMipMaps is on, the remaining levels are
 * regenerated by the engine. Uploading the file's own mip chain needs per-mip array uploads, which WebGL's
 * updateRawTexture2DArray does not support today.
 * @param scene defines the hosting scene
 * @param data defines the url of the KTX2 file, or its already fetched content
 * @param options defines optional creation settings
 * @returns a promise resolved with the created RawTexture2DArray
 */
export async function CreateTexture2DArrayFromKTX2Async(scene: Scene, data: string | ArrayBufferView, options?: ICreateTexture2DArrayFromKTX2Options): Promise<RawTexture2DArray> {
    let buffer: ArrayBufferView;
    if (typeof data === "string") {
        const response = await fetch(data);
        if (!response.ok) {
            throw new Error(`Failed to fetch KTX2 file "${data}": ${response.status} ${response.statusText}`);
        }
        buffer = new Uint8Array(await response.arrayBuffer());
    } else {
        buffer = data;
    }

    const { KhronosTextureContainer2 } = await import("../../Misc/khronosTextureContainer2");

    if (!KhronosTextureContainer2.IsValid(buffer)) {
        throw new Error("The provided data is not a valid KTX2 file.");
    }

    const container = new KhronosTextureContainer2(scene.getEngine());

    // forceRGBA: the transcoded compressed formats cannot be uploaded to an array texture yet (see above).
    const decodedData = await container._decodeAsync(buffer, { forceRGBA: true });

    if (decodedData.errors) {

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Verify the KTX2 URL returns 200 (curl -I)
  2. Ensure the KTX2 asset is included in the build/deploy output
  3. Serve the file from an accessible origin or pre-fetch with credentials and pass the ArrayBufferView instead of a string
  4. Fall back to the ArrayBufferView overload when a URL cannot be authenticated

Example fix

// before
const tex = await CreateTexture2DArrayFromKTX2Async(scene, "ktx2/layers.ktx2");
// after
const res = await fetch("ktx2/layers.ktx2");
if (!res.ok) throw new Error(`ktx2/layers.ktx2 unavailable: ${res.status}`);
const tex = await CreateTexture2DArrayFromKTX2Async(scene, new Uint8Array(await res.arrayBuffer()));
Defensive patterns

Strategy: retry

Validate before calling

async function assertKtx2Fetchable(url: string): Promise<void> {
  const res = await fetch(url, { method: "HEAD" });
  if (!res.ok) throw new Error(`KTX2 ${url} not available: ${res.status}`);
}

Try / catch

try {
  const tex = await CreateTexture2DArrayFromKTX2Async(scene, ktx2Url);
} catch (e) {
  if (e instanceof Error && e.message.startsWith("Failed to fetch KTX2")) {
    console.error(`KTX2 missing: ${ktx2Url}`);
    return createFallbackArrayTexture(scene);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a KTX2 URL that returns 404/403/500 — wrong path, asset not deployed, missing permission, or server error.

Common situations: Assets not copied to the build output, wrong bucket/CDN path, or tokens required for the KTX2 file (this fetch sends no auth headers).

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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