BabylonJS/Babylon.js · error
clientWaitSync failed
Error message
clientWaitSync failed
What it means
This comes from the engine's async readback path (based on gl.clientWaitSync / fence sync). If the sync object wait returns WAIT_FAILED — the sync object is invalid, the context is lost, or the fence was never signaled properly — the retry loop throws "clientWaitSync failed".
Source
Thrown at packages/dev/core/src/Engines/engine.pure.ts:1006
return result;
}
/**
* Delete a webGL buffer used with instantiation
* @param buffer defines the webGL buffer to delete
*/
public deleteInstancesBuffer(buffer: WebGLBuffer): void {
this._gl.deleteBuffer(buffer);
}
private async _clientWaitAsync(sync: WebGLSync, flags = 0, intervalms = 10): Promise<void> {
const gl = <WebGL2RenderingContext>(this._gl as any);
return await new Promise((resolve, reject) => {
_RetryWithInterval(
() => {
const res = gl.clientWaitSync(sync, flags, 0);
if (res == gl.WAIT_FAILED) {
throw new Error("clientWaitSync failed");
}
if (res == gl.TIMEOUT_EXPIRED) {
return false;
}
return true;
},
resolve,
reject,
intervalms
);
});
}
/**
* This function might return null synchronously, so it is technically not async.
* @internal
*/
// eslint-disable-next-line no-restricted-syntax
View on GitHub (pinned to 0592b347b8)
Solutions
- Check gl.isContextLost() in the catch handler; if lost, rebuild the engine and retry the readback after restore.
- Ensure the texture/framebuffer still exists and wasn't disposed between issuing the readback and awaiting the promise.
- Retry the readback operation once after a frame; transient WAIT_FAILED can occur on some drivers.
- Fall back to synchronous readPixels if async readback repeatedly fails.
Example fix
// before
const data = await engine._readPixelsAsync(x, y, w, h, fmt, type, buffer);
// after
try {
const data = await engine._readPixelsAsync(x, y, w, h, fmt, type, buffer);
} catch (e) {
if (String(e).includes("clientWaitSync")) {
engine._gl.isContextLost()
? rebuildEngineAndRedoReadback()
: readPixelsFallback();
}
} Defensive patterns
Strategy: retry
Validate before calling
function canAwaitReadback(engine) {
return engine.webGLVersion >= 2 && !engine._gl.isContextLost();
} Type guard
function isReadbackSafe(engine, sync) {
return engine.webGLVersion >= 2 && !engine._gl.isContextLost() && !!sync;
} Try / catch
try {
await readbackPromise;
} catch (e) {
if (/clientWaitSync failed/.test(e.message)) {
if (engine._gl.isContextLost()) scheduleEngineRebuild();
else retryReadbackOnce();
}
} Prevention
- Await readbacks promptly; don't dispose textures/framebuffers between issue and await.
- Monitor webglcontextlost so in-flight fences are invalidated.
- Keep a synchronous readPixels fallback path.
When it happens
Trigger: Awaiting _readPixelsAsync (or similar fence-based GPU->CPU readback) when the WebGL2 context is lost, the sync object was created on a different context, or a driver-level fence error occurs.
Common situations: Background tab / GPU process crash mid-readback, mobile browser context loss during long-running apps, calling readback after the engine or texture was disposed.
Related errors
- Unable to create Transform Feedback
- Unable to create uniform buffer
- _readPixelsAsync only work on WebGL2+
- Recast is not initialized. Please call InitRecast first.
- Unable to generate navMesh: ${e}
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/79fe451a76323d42.
Report an issue: GitHub.