BabylonJS/Babylon.js · error

updateWrappedNativeTexture: target InternalTexture was not p

Error message

updateWrappedNativeTexture: target InternalTexture was not produced by wrapNativeTexture.

What it means

updateWrappedNativeTexture can only repoint an InternalTexture whose source is InternalTextureSource.External, i.e. one created by wrapNativeTexture. The guard exists because repointing a non-external texture would bypass ownership of its native handle and corrupt engine state.

Source

Thrown at packages/dev/core/src/Engines/thinNativeEngine.pure.ts:2158

     * Intended for the device-loss / device-restored flow (a DisableRendering / EnableRendering cycle from the host
     * application): when the host recreates its external resource on the new graphics device, it calls this method to
     * repoint Babylon's wrapper at the new handle without losing references held by materials, render-target wrappers,
     * particle systems, etc.
     *
     * The new handle must match the wrapped texture's recorded dimensions. To change dimensions, dispose the wrapped
     * texture and call {@link wrapNativeTexture} again. Sampling mode and mip-map flag are properties of the logical
     * wrapped texture and are re-applied to the new resource. Any render-target wrapper holding this texture as its
     * color attachment has its framebuffer rebuilt with the new handle.
     *
     * Throws if the target was not produced by {@link wrapNativeTexture}, if the new handle's dimensions don't match,
     * if the wrapped texture is part of a multi render-target, or if the wrapper has a depth/stencil texture (these
     * are not supported in this version; dispose and re-wrap).
     * @param internalTexture defines the wrapped InternalTexture to repoint
     * @param texture defines the new native texture handle to wrap
     */
    public updateWrappedNativeTexture(internalTexture: InternalTexture, texture: NativeTexture): void {
        if (internalTexture.source !== InternalTextureSource.External) {
            throw new Error("updateWrappedNativeTexture: target InternalTexture was not produced by wrapNativeTexture.");
        }

        const newWidth = this._engine.getTextureWidth(texture);
        const newHeight = this._engine.getTextureHeight(texture);
        if (newWidth !== internalTexture.baseWidth || newHeight !== internalTexture.baseHeight) {
            throw new Error(
                `updateWrappedNativeTexture: new handle dimensions (${newWidth}x${newHeight}) must match the wrapped texture's dimensions (${internalTexture.baseWidth}x${internalTexture.baseHeight}).`
            );
        }
        if (this._engine.getTextureLayerCount) {
            const newLayerCount = this._engine.getTextureLayerCount(texture);
            const oldLayerCount = internalTexture.is2DArray ? internalTexture.depth : 1;
            if (newLayerCount !== oldLayerCount) {
                throw new Error(`updateWrappedNativeTexture: new handle layer count (${newLayerCount}) must match the wrapped texture's layer count (${oldLayerCount}).`);
            }
        }

        // Pre-validate before mutating any state so a thrown precondition leaves the InternalTexture untouched.

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Only pass InternalTextures returned from engine.wrapNativeTexture().
  2. If you need to swap the underlying data of a regular texture, recreate it (dispose + create) instead.
  3. Track wrapped textures explicitly (e.g. keep the value returned by wrapNativeTexture) rather than reading texture from a render target.

Example fix

// before
engine.updateWrappedNativeTexture(rt.texture, newNativeTex); // rt texture is not wrapped

// after
const wrapped = engine.wrapNativeTexture(oldNativeTex, width, height, ...);
engine.updateWrappedNativeTexture(wrapped, newNativeTex);
Defensive patterns

Strategy: validation

Validate before calling

if (internalTexture.source !== BABYLON.InternalTextureSource.External) {
  throw new Error("updateWrappedNativeTexture requires a texture from wrapNativeTexture");
}

Type guard

const isWrappedNativeTexture = (t: BABYLON.InternalTexture): boolean =>
  t.source === BABYLON.InternalTextureSource.External;

Try / catch

try {
  engine.updateWrappedNativeTexture(tex, newNativeTex);
} catch (e) {
  if (e.message.includes("not produced by wrapNativeTexture")) {
    console.warn("Recreating texture instead of updating", e);
    recreateTexture(tex);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling engine.updateWrappedNativeTexture(internalTexture, nativeTexture) on a texture created via createTexture/createRawTexture or a render-target texture (source !== External).

Common situations: Keeping a plain created texture in a variable that also holds wrapped textures and passing the wrong one; assuming any InternalTexture can be updated.

Related errors


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