# GLB export broken under NullEngine in v9 - root cause and fix

**URL:** <https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231>\
**Category:** Bugs\
**Tags:** gltf, glb, nullengine, gltf2export, bug\
**Created:** [April 21, 2026, 9:14am UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231 "2026-04-21T09:14:56Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Arek3D](https://avatars.discourse-cdn.com/v4/letter/a/b9e5f3/32.png) [@Arek3D](https://forum.babylonjs.com/u/Arek3D)\
**Post date:** [April 21, 2026, 9:14am UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/1 "2026-04-21T09:14:56Z")

</div>

Hi, I found a bug with GLB export under NullEngine on the server. It worked correctly in v8, but after upgrading to v9 the following error appears:

```auto
Error: Failed to read pixels from texture <name>.
    at GetTextureDataAsync

```

I traced the regression to an optimization: `GetCachedImageAsync` in `glTFMaterialExporter.ts` (and a parallel version in `usdzExporter.ts` which we already merged).

### Root Cause

Both functions contain this guard:

```auto
if (internalTexture.invertY) {
    return null;
}

```

`invertY` defaults to `true` for every texture loaded from a URL — so this guard always fires, returns `null`, and forces fallback to `GetTextureDataAsync`. That function reads pixels from the GPU, which requires a WebGL context. Under NullEngine there is no GPU → exception.

The intent of the cached path was precisely to avoid GPU readback (faster, works without WebGL). The `invertY` guard defeats its own purpose.

### Why the Guard is Wrong

- `_buffer` always stores the original source bytes of the image file — before any GPU upload

- `invertY` is a WebGL upload-time operation that has no bearing on the source bytes

- For export purposes (embedding a PNG/JPEG blob in a glTF/USDZ archive), the original bytes are exactly what we want

- A glTF/USDZ viewer handles texture orientation itself

Ironically, the GPU fallback path may produce worse results: it reads back pixels after the Y-flip, yielding an upside-down image in the archive.

### Fix

Make the `invertY` guard context-aware:

```auto
if (internalTexture.invertY) {
    // On a real engine, the GPU has the texture stored flipped (UNPACK_FLIP_Y_WEBGL),
    // while the glTF loader uploads with invertY=false. Falling back to GPU readback
    // produces bytes that round-trip correctly. NullEngine has no GPU readback path,
    // so the cached URL bytes are the only option.
    const engine = babylonTexture.getScene()?.getEngine();
    if (!(engine instanceof NullEngine)) {
        return null;
    }
}

```

This preserves correct GPU readback behavior on WebGL engines while allowing NullEngine to use the cached bytes.

### PR Status

Both fixes have been submitted:

- **glTF fix:** [PR #18357](https://github.com/BabylonJS/Babylon.js/pull/18357) — ⏳ Pending review

- **USDZ fix:** [PR #18296](https://github.com/BabylonJS/Babylon.js/pull/18296) — ✅ **Merged**

**Implementation:** Both GLB and USDZ export now work correctly under NullEngine using the [arek-3d/Babylon.js fork](https://github.com/arek-3d/Babylon.js/tree/gltf-nullengine-cached-image).

* * *

_Note: USDZ didn’t work on server even in v8, but GLB worked properly. See this thread regarding USDZ server-side support._

---

<div class="post-metadata">

**Author:** ![Deltakosh](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/deltakosh/32/26635_2.png) [@Deltakosh](https://forum.babylonjs.com/u/Deltakosh)\
**Post date:** [April 21, 2026, 3:58pm UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/2 "2026-04-21T15:58:34Z")

</div>

cc @alexchuber

---

<div class="post-metadata">

**Author:** ![alexchuber](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/alexchuber/32/52000_2.png) [@alexchuber](https://forum.babylonjs.com/u/alexchuber)\
**Post date:** [April 22, 2026, 4:42pm UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/3 "2026-04-22T16:42:16Z")

</div>



---

<div class="post-metadata">

**Author:** ![alexchuber](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/alexchuber/32/52000_2.png) [@alexchuber](https://forum.babylonjs.com/u/alexchuber)\
**Post date:** [April 22, 2026, 5:20pm UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/4 "2026-04-22T17:20:58Z")

</div>

Thanks for the contributions! Will give this one a review soon.

In the meantime, could you help me understand the details of the regression a bit more? I want to make sure we’re not missing anything else 🙂

To my knowledge, the glTF exporter shouldn’t have been able to export textures at all using NullEngine-- at least not until the alternate `GetCachedImageAsync` path was introduced in v8.23.2. Were you using a version earlier or later than this?

---

<div class="post-metadata">

**Author:** ![Arek3D](https://avatars.discourse-cdn.com/v4/letter/a/b9e5f3/32.png) [@Arek3D](https://forum.babylonjs.com/u/Arek3D)\
**Post date:** [April 22, 2026, 7:32pm UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/5 "2026-04-22T19:32:02Z")

</div>

I’m pretty sure I was using version 8.33 in a Node.js application, where GLB export with textures was working fine. However, USDZ export was throwing an error (readPixels did not work on NullEngine).

I created a topic about it here:

> [@Server-Side USDZ Export Support in Babylon.js?](https://forum.babylonjs.com/t/server-side-usdz-export-support-in-babylon-js/60872):
>
> Hi, I’m wondering if there are any plans to make the USDZ exporter in Babylon.js work in a server-side / Node.js environment. The GLB exporter can be adapted to run outside the browser using NullEngine and Node.js, but the USDZ exporter seems to be much more tightly coupled to WebGL and the canvas context - specifically the “bitmaprenderer” mode used inside: ”@babylonjs/core/Misc/dumpTools.js” Which causes this error: ”USDZ export failed: Error: DumpData: No WebGL or bitmap rendering con…

Based on the feedback I received from you, I made a fork and submitted a PR to add USDZ support. That was done on version 9, and it worked correctly for USDZ files.

After upgrading my Node.js project to Babylon.js v9, I noticed that GLB export no longer works. I’m now getting the same readPixels error that previously only occurred with the USDZ exporter in v8.33.

Has something changed in v9 regarding gltf exporter that could affect this function?

---

<div class="post-metadata">

**Author:** ![alexchuber](https://sea2.discourse-cdn.com/flex024/user_avatar/forum.babylonjs.com/alexchuber/32/52000_2.png) [@alexchuber](https://forum.babylonjs.com/u/alexchuber)\
**Post date:** [April 30, 2026, 1:55pm UTC](https://forum.babylonjs.com/t/glb-export-broken-under-nullengine-in-v9-root-cause-and-fix/63231/6 "2026-04-30T13:55:51Z")

</div>

Gotcha. Yeah, sounds like it was indeed a gap surrounding NullEngine and both the USDZ and glTF exporters. Makes sense. Thanks for the contribution!
