After upgrading from Babylon.js 8.53.1 to 8.54.1, we see a regression that breaks rendering and cleanup. Rolling back to 8.53.1 fixes it.
Error:
TypeError: i2._releaseRenderPassId is not a function
at r2.$.releaseRenderPassId (…)
at e3._releaseRenderPassId (…)
at e3.dispose (…)
at t3.dispose (…)
at e2._disposeTextureAndPostProcesses (…)
at e2.render (…)
at e2._draw (…)
at e2._drawCamera (…)
at e3._renderForCamera (…)
at e3._processSubCameras (…)
The error is thrown during the normal render loop when the engine/camera code disposes textures or post-processes and calls _releaseRenderPassId on the engine. In 8.54.1 the engine instance we get (e.g. from scene.getEngine() ) does not have _releaseRenderPassId , so the call fails.
Observed impact:
Shadows (e.g. ShadowGenerator) stop working or throw during dispose.
Lighting and related effects are affected (e.g. glow, volumetric/post-processes).
Dispose of meshes, materials, and render targets is unreliable: disposal paths that go through _releaseRenderPassId throw, so cleanup is incomplete and we see leaks or follow-up errors.
Environment:
Babylon.js 8.54.1 (bundled with Vite; we use both the babylonjs package and @babylonjs/core).
new Engine(canvas, ...) for the main engine; the error occurs when that engine is used for rendering and during dispose (e.g. camera/post-process/texture cleanup).
Workaround: We temporarily stayed on 8.53.1, where _releaseRenderPassId is present on the engine and the error does not occur.
Could you check whether 8.54.1 changed how _releaseRenderPassId is defined or attached to the engine (e.g. only on a specific engine subclass or in a code path we don’t hit), and restore the previous behavior so disposal and rendering work again?
_releaseRenderPassId is a method of AbstractMesh, not AbstractEngine, which has a releaseRenderPassId method. It’s during the execution of this method that AbstractMesh._releaseRenderPassId is called:
AbstractEngine.prototype.releaseRenderPassId = function (id: number): void {
this._renderPassNames[id] = undefined as any;
for (let s = 0; s < this.scenes.length; ++s) {
const scene = this.scenes[s];
for (let m = 0; m < scene.meshes.length; ++m) {
const mesh = scene.meshes[m];
mesh._releaseRenderPassId(id);
if (mesh.subMeshes) {
for (let b = 0; b < mesh.subMeshes.length; ++b) {
const subMesh = mesh.subMeshes[b];
subMesh._removeDrawWrapper(id);
}
}
}
}
};
Are you sure all meshes in scene.meshes have AbstractMesh as their base class? The only way I can see the code crashing is if a mesh doesn’t subclass AbstractMesh (which would be an error, because Scene.meshes is declared as AbstractMesh[]);
I tried to reproduce this one:
The shadow generator is disposed after 1s, but it doesn’t crash.
@Evgeni_Popov Thank you so much for your assistance.
IT IS NOT A BABYLONJS BUG.
The problem was definitely rooted in the update, but the real culprit was my package.json , which contained conflicting dependencies . I just got lucky in the past that they happened to work the same way.
We’ve tracked down the root cause on our side and it was not a Babylon.js bug but a bundling issue.
Our app was using both babylonjs and @babylonjs/core at the same time:
All rendering code (Engine, Scene, Mesh, ShadowGenerator, WaterMaterial, etc.) imported from babylonjs (+ babylonjs-materials).
A single type import Sound was coming from @babylonjs/core/Audio.
With Vite this ended up pulling two independent copies of Babylon into the same bundle. Internal render‑pass code (renderPassIds / _releaseRenderPassId) assumed that all objects live in the same world, but in practice some objects came from the babylonjs instance and others from the @babylonjs/core instance. That mismatch eventually led to calling _releaseRenderPassId on an object that didn’t have it in its prototype chain, which is what produced: TypeError: i2._releaseRenderPassId is not a function
This also explains why we could not reproduce the error in the Playground: the Playground always uses a single global Babylon instance, so this clash never happens there.
The fix on our side was:
Remove all runtime usage of @babylonjs/core (we only had one type import – we switched Sound to import type { Sound } from 'babylonjs').
Remove @babylonjs/core from dependencies, keeping only the UMD packages: