BabylonJS/Babylon.js · error · Error
No volume subnode
Error message
No volume subnode
What it means
The volume setter on AbstractAudioOutNode (implemented by sounds, sound sources, and buses in Babylon.js Audio V2) assumes a volume audio subnode was created when the node was initialized, as the in-code comment states. _GetVolumeAudioSubNode(this._subGraph) returned null, so assigning node.volume throws "No volume subnode". This means the node's sub-graph was never initialized or has already been disposed.
Source
Thrown at packages/dev/core/src/AudioV2/abstractAudio/abstractAudioOutNode.ts:43
/**
* The audio analyzer features.
*/
public get analyzer(): AbstractAudioAnalyzer {
return this._analyzer ?? (this._analyzer = new _AudioAnalyzer(this._subGraph));
}
/**
* The audio output volume.
*/
public get volume(): number {
return _GetVolumeAudioProperty(this._subGraph, "volume");
}
public set volume(value: number) {
// The volume subnode is created on initialization and should always exist.
const node = _GetVolumeAudioSubNode(this._subGraph);
if (!node) {
throw new Error("No volume subnode");
}
node.volume = value;
}
/**
* Releases associated resources.
*/
public override dispose(): void {
super.dispose();
this._analyzer?.dispose();
this._analyzer = null;
this._subGraph.dispose();
}
/**View on GitHub (pinned to 0592b347b8)
Solutions
- Wait for node readiness before setting volume — e.g. use the async creation API (await SoundV2.CreateAsync / engine.createSoundAsync) so the subgraph and its volume subnode exist.
- Check that the node has not been disposed; do not touch volume after calling dispose() or after the engine is released.
- Verify the Web Audio context/engine is still running; a suspended/closed engine can prevent subnode creation.
- If you cannot guarantee timing, wrap the assignment in try/catch and treat "No volume subnode" as 'node not ready or disposed'.
Example fix
// before
const sound = await engine.createSoundAsync(url);
sound.dispose();
sound.volume = 0.5; // throws
// after
const sound = await engine.createSoundAsync(url);
if (!sound.isDisposed) {
sound.volume = 0.5;
} Defensive patterns
Strategy: try-catch
Validate before calling
// set volume only when the node is ready and not disposed
if (!sound.isDisposed && engine.state === "Started") {
sound.volume = 0.5;
} Type guard
function canSetVolume(node) {
return node != null && !node.isDisposed && typeof node.volume === "number";
} Try / catch
try {
sound.volume = 0.5;
} catch (e) {
if (e.message === "No volume subnode") {
// node not initialized or already disposed; retry after ready or skip
} else {
throw e;
}
} Prevention
- Await async creation APIs (createSoundAsync / CreateAsync) before touching volume.
- Null out UI bindings, timers, and animation callbacks when the node is disposed.
- Check engine/context state after tab suspension or user-gesture resume before setting volume.
- Treat any post-dispose access as a bug: guard all writes with an isDisposed check.
When it happens
Trigger: Setting `node.volume = x` on an audio node whose _subGraph has no volume subnode — typically after calling dispose() on the node, before async initialization of the subgraph completed, or if the subgraph failed to create its subnodes (e.g. audio engine/context not ready).
Common situations: Setting volume after sound.dispose() in a UI callback firing late; setting volume immediately after construction before the sound's internal async creation promise resolves; engine shutdown or context loss tearing down subnodes while app code still holds the node reference.
Related errors
- Disconnect failed
- The value passed to [Scene.audioListenerPositionProvider] mu
- The value passed to [Scene.audioListenerRotationProvider] mu
- Disconnect failed
- Disconnect failed
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/5b972f2735626f9c.
Report an issue: GitHub.