linera-io/linera-protocol · error · JsError
Client disposed while being referenced elsewhere
Error message
Client disposed while being referenced elsewhere
What it means
`Client.asyncDispose()` (exposed to JS as `asyncDispose`) shuts down the web client: it stops the chain listener, then consumes the internal `Arc<ClientContext>` with `Arc::into_inner`. That only succeeds when this instance holds the last reference; every `Chain` object created via `client.chain(chainId)` clones the context, so any live `Chain` (or Client clone) keeps a reference alive and `Arc::into_inner` returns `None`, producing this error.
Source
Thrown at web/@linera/client/src/client.rs:161
})
}
/// Cleanly shut down the client, completing when it is destroyed and all
/// resources it owns are released.
///
/// # Errors
///
/// If the context is being referenced by any other objects (chains,
/// applications…). Free these with `.free()` before disposing of this
/// object.
///
/// Propagates any errors that occurred during background execution of the
/// client.
#[wasm_bindgen(js_name = asyncDispose)]
pub async fn async_dispose(self) -> Result<()> {
self.stop().await?;
let context = Arc::into_inner(self.context).ok_or(Error::new(
"Client disposed while being referenced elsewhere",
))?;
drop(context);
Ok(())
}
}
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Call `.free()` on every Chain (and any other handle) obtained from the client before calling `asyncDispose()`.
- Centralize teardown: free all tracked chains in one place, then dispose the client.
- Keep an array of created chains and free them in a loop right before disposal.
- If the error persists, hunt for forgotten Client clones or unresolved promises holding a Chain.
Example fix
// before const chain = client.chain(chainId); await client.asyncDispose(); // 'Client disposed while being referenced elsewhere' // after const chains = [client.chain(chainId)]; // ... use chains ... for (const c of chains) c.free(); await client.asyncDispose();
Defensive patterns
Strategy: validation
Validate before calling
const openChains = new Set();
function acquireChain(id) { const c = client.chain(id); openChains.add(c); return c; }
async function disposeClient() { for (const c of openChains) c.free(); openChains.clear(); await client.asyncDispose(); } Type guard
function allChainsFreed(tracked: Set<Chain>): boolean { return tracked.size === 0; } Try / catch
try { await client.asyncDispose(); } catch (e) { if (/disposed while being referenced/i.test(e.message)) { freeAllChains(); await client.asyncDispose(); } else throw e; } Prevention
- Track every Chain handle created from client.chain() and free() it in teardown.
- Dispose the client only from a single top-level teardown routine.
- Avoid cloning the Client into long-lived structures.
- On error paths, free chains before rethrowing.
When it happens
Trigger: Calling `await client.asyncDispose()` while one or more `Chain` handles from `client.chain(...)` are still alive; keeping another clone of the Client somewhere; a long-lived promise still holding a Chain during teardown.
Common situations: Web dApp teardown paths disposing the client before per-chain handles; UI components unmounting in the wrong order; error paths that skip cleanup of chain handles.
Related errors
- chain {chain_id} doesn't exist in wallet
- A Wasm runtime is required to load user applications. Please
- BytecodeTooLarge
- MaximumFuelExceeded
- ReentrantCall
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/f330f11f21dbaba3.
Report an issue: GitHub.