BabylonJS/Babylon.js · error · Error
Disconnect failed
Error message
Disconnect failed
What it means
AbstractAudioNode.dispose() tears down the audio graph by disconnecting every downstream node before clearing its bookkeeping. It calls the internal _disconnect(node) for each downstream node, and if any of those returns false (the node was not registered as downstream, this node has no downstream set, or the other node's _onDisconnect rejected), dispose() throws "Disconnect failed" instead of silently leaking the connection. This indicates the audio graph bookkeeping is out of sync with what dispose() expects.
Source
Thrown at packages/dev/core/src/AudioV2/abstractAudio/abstractAudioNode.ts:67
if (nodeType & AudioNodeType.HAS_INPUTS) {
this._upstreamNodes = new Set<AbstractAudioNode>();
}
if (nodeType & AudioNodeType.HAS_OUTPUTS) {
this._downstreamNodes = new Set<AbstractAudioNode>();
}
}
/**
* Releases associated resources.
* - Triggers `onDisposeObservable`.
* @see {@link onDisposeObservable}
*/
public dispose(): void {
if (this._downstreamNodes) {
for (const node of Array.from(this._downstreamNodes)) {
if (!this._disconnect(node)) {
throw new Error("Disconnect failed");
}
}
this._downstreamNodes.clear();
}
if (this._upstreamNodes) {
for (const node of Array.from(this._upstreamNodes)) {
if (!node._disconnect(this)) {
throw new Error("Disconnect failed");
}
}
this._upstreamNodes.clear();
}
this.onDisposeObservable.notifyObservers(this);
this.onDisposeObservable.clear();
}
View on GitHub (pinned to 0592b347b8)
Solutions
- Dispose nodes in dependency order — disconnect children/downstream nodes first (or ensure the downstream node is not already disposed) before calling dispose() on parents.
- Check whether the downstream node was already disposed and remove it from tracking yourself instead of relying on dispose() to unlink it.
- If you wrote a custom subclass, verify it passes the correct AudioNodeType so _downstreamNodes/_upstreamNodes sets exist, and that its _onDisconnect override returns true for valid removals.
- As a last resort, wrap dispose() in try/catch since the throw happens after partial teardown; the node's onDisposeObservable may not have fired, so clean up listeners manually.
Example fix
// before sound.dispose(); bus.dispose(); // bus still connected downstream of sound can make sound's dispose throw earlier // after bus.disconnect(sound); // or dispose bus first if it owns the link bus.dispose(); sound.dispose();
Defensive patterns
Strategy: try-catch
Validate before calling
// before disposing, verify the node owns live downstream links
function canDisposeSafely(node) {
return Array.from(node._downstreamNodes ?? []).every(
(peer) => !peer.isDisposed && (peer._upstreamNodes?.has(node) ?? false)
);
} Type guard
function isConnectedDownstream(node, peer) {
return node instanceof AbstractAudioNode &&
Array.from(node._downstreamNodes ?? []).includes(peer);
} Try / catch
try {
node.dispose();
} catch (e) {
if (e.message === "Disconnect failed") {
// graph already inconsistent; drop references and continue teardown
node.onDisposeObservable.clear();
} else {
throw e;
}
} Prevention
- Dispose the audio graph top-down (outputs before sources) and never dispose a node twice.
- Use only the public connect()/disconnect() APIs; avoid touching _downstreamNodes or internals.
- Remove your onDisposeObservable callbacks so disposed peers don't leave stale links.
- In custom node subclasses, pass the AudioNodeType that matches how the node is actually wired.
When it happens
Trigger: Calling dispose() on an audio node whose _downstreamNodes set contains a node for which this._disconnect(node) returns false — i.e. the downstream node has no _upstreamNodes set (wrong AudioNodeType), or its _onDisconnect callback refuses the removal (e.g. the peer node was already disposed or mutated externally). Also thrown when a subclass overrides _disconnect/_onDisconnect and returns false.
Common situations: Disposing nodes in an order or from multiple code paths that double-dispose; manually manipulating connections via internals instead of connect()/disconnect(); custom node subclasses constructed with a nodeType that doesn't match how they're connected (e.g. an input-only node connected as downstream); engine teardown while another callback already removed the link.
Related errors
- Disconnect failed
- The value passed to [Scene.audioListenerPositionProvider] mu
- The value passed to [Scene.audioListenerRotationProvider] mu
- No volume subnode
- Connect failed
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/ed724b758a124a52.
Report an issue: GitHub.