{"record":{"id":"67fde198777053bb","repo":"unoplatform/uno","slug":"clientwaitsync-failed-status-0x-syncstatus-x","errorCode":null,"errorMessage":"ClientWaitSync failed (status=0x{syncStatus:x})","messagePattern":"ClientWaitSync failed \\(status=0x(.+?)\\)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/QuerySyncGlCanvasElement.cs","lineNumber":197,"sourceCode":"\t\t\t{\n\t\t\t\tgl.DeleteSync(_frameSync);\n\t\t\t\t_frameSync = 0;\n\t\t\t}\n\t\t}\n\n\t\tprotected override unsafe void RenderOverride(GL gl)\n\t\t{\n\t\t\tvar t = (float)(DateTime.UtcNow - _startTime).TotalSeconds;\n\n\t\t\t// --- Check last frame's fence; the GPU should long be done by the next paint ---\n\t\t\tif (_frameSync != 0)\n\t\t\t{\n\t\t\t\t// WebGL2 requires a 0 timeout for ClientWaitSync.\n\t\t\t\tvar waitResult = (GLEnum)gl.ClientWaitSync(_frameSync, (SyncObjectMask)0, 0);\n\t\t\t\tgl.GetSync(_frameSync, SyncParameterName.SyncStatus, 1, out _, out int syncStatus);\n\t\t\t\tif (waitResult == GLEnum.WaitFailed)\n\t\t\t\t{\n\t\t\t\t\tthrow new Exception($\"ClientWaitSync failed (status=0x{syncStatus:x})\");\n\t\t\t\t}\n\t\t\t\tgl.DeleteSync(_frameSync);\n\t\t\t\t_frameSync = 0;\n\t\t\t}\n\n\t\t\t// --- Poll the occlusion query from a previous frame without stalling ---\n\t\t\tif (_queryInFlight)\n\t\t\t{\n\t\t\t\tgl.GetQueryObject(_query, QueryObjectParameterName.ResultAvailable, out uint available);\n\t\t\t\tif (available != 0)\n\t\t\t\t{\n\t\t\t\t\tgl.GetQueryObject(_query, QueryObjectParameterName.Result, out uint anySamplesPassed);\n\t\t\t\t\t_lastProbeVisible = anySamplesPassed != 0;\n\t\t\t\t\t_queryInFlight = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgl.ClearColor(0.06f, 0.06f, 0.08f, 1f);","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/unoplatform/uno/blob/04183404888ea21b4e5bfa3493e8d5f15e972c3a/src/SamplesApp/SamplesApp.Samples/Windows_UI_Composition/QuerySyncGlCanvasElement.cs#L179-L215","documentation":"In RenderOverride, QuerySyncGlCanvasElement checks the previous frame's fence sync via gl.ClientWaitSync with a 0 timeout (required by WebGL2). If the return is GL_WAIT_FAILED the sync is broken — the GPU could not signal completion, typically due to context loss, an invalid sync object, or a GPU reset. The sync status from GetSync is included in the message.","triggerScenarios":"When _frameSync != 0 (a fence was inserted last frame), gl.ClientWaitSync(_frameSync, 0, 0) returns GLEnum.WaitFailed (lines 193-197). The accompanying GetSync(SyncStatus) value is appended.","commonSituations":"The GL context was lost (WebGL context-loss event) and the sync object is now invalid; a GPU reset (TDR on Windows, watchdog on mobile) invalidated outstanding syncs; the sync object was already deleted and _frameSync was not cleared; on some drivers ClientWaitSync with timeout 0 returns WAIT_FAILED if the GPU has not started the commands yet rather than TIMEOUT_EXPIRED (driver bug); WebGL2 where fence syncs have stricter semantics.","solutions":["Check for context loss: on WebGL listen for the webglcontextlost event; on native query gl.GetGraphicsResetStatus() (or the GLES counterpart) to detect a GPU reset.","Verify _frameSync is non-zero and was returned by a recent FenceSync call before ClientWaitSync, and clear it to 0 immediately after DeleteSync so it is not reused.","Treat WAIT_FAILED as recoverable: delete the failed sync, reset _frameSync = 0, skip the frame, and re-fence on the next render rather than throwing.","If the GetSync status is SIGNALED but ClientWaitSync still returned WAIT_FAILED, suspect a driver bug — file against the vendor and downgrade the throw to a warning.","On WebGL2, ensure the sync is created on the same context that waits on it (cross-context syncs are invalid)."],"exampleFix":"// before\nvar waitResult = (GLEnum)gl.ClientWaitSync(_frameSync, (SyncObjectMask)0, 0);\ngl.GetSync(_frameSync, SyncParameterName.SyncStatus, 1, out _, out int syncStatus);\nif (waitResult == GLEnum.WaitFailed)\n    throw new Exception($\"ClientWaitSync failed (status=0x{syncStatus:x})\");\ngl.DeleteSync(_frameSync); _frameSync = 0;\n\n// after — recover instead of throwing on transient failure\nvar waitResult = (GLEnum)gl.ClientWaitSync(_frameSync, (SyncObjectMask)0, 0);\nif (waitResult == GLEnum.WaitFailed)\n{\n    gl.GetSync(_frameSync, SyncParameterName.SyncStatus, 1, out _, out int syncStatus);\n    System.Diagnostics.Debug.WriteLine($\"ClientWaitSync failed (status=0x{syncStatus:x}); resetting fence\");\n    gl.DeleteSync(_frameSync); _frameSync = 0; // recover, skip gating this frame\n}","handlingStrategy":"validation","validationCode":"// Verify the sync object is valid and check for context loss before waiting\nif (_frameSync == 0) return;\n// On WebGL, listen for webglcontextlost; on native, query graphics reset status\ngl.GetSync(_frameSync, SyncParameterName.ObjectType, 1, out _, out int objType);\nif (objType != (int)GLEnum.SyncFence) { _frameSync = 0; return; } // stale/invalid sync","typeGuard":null,"tryCatchPattern":"try\n{\n    var waitResult = (GLEnum)gl.ClientWaitSync(_frameSync, (SyncObjectMask)0, 0);\n    if (waitResult == GLEnum.WaitFailed) throw new Exception($\"status=0x{syncStatus:x}\");\n}\ncatch (Exception ex) when (ex.Message.Contains(\"ClientWaitSync failed\"))\n{\n    App.MainWindow?.LogError($\"ClientWaitSync failed: {ex.Message}\");\n    gl.DeleteSync(_frameSync); _frameSync = 0; // recover, skip gating this frame\n}","preventionTips":["Detect context loss (WebGL event / GetGraphicsResetStatus) and reset sync state.","Clear _frameSync to 0 immediately after DeleteSync so it is never reused stale.","Treat WAIT_FAILED as recoverable: reset and continue rather than throwing.","On WebGL2, create and wait on syncs within the same context."],"tags":["opengl","silk-net","fence-sync","client-wait-sync","webgl2","context-loss","gpu-reset"],"backgroundTag":null,"analyzedSha":"04183404888ea21b4e5bfa3493e8d5f15e972c3a","analyzedAt":"2026-08-13T21:08:11.651Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}