unoplatform/uno · error · InvalidOperationException

The framebuffer readback failed with {readbackError} while c

Error message

The framebuffer readback failed with {readbackError} while copying the framebuffer to the back buffer.

What it means

After glReadPixels copies the offscreen framebuffer into the back buffer, GLCanvasElement checks _gl.GetError(); any non-NoError code throws InvalidOperationException with the offending GL enum. glReadPixels itself does not throw — it silently sets a GL error and leaves the destination blank, so this explicit check converts a silent blank-canvas failure into a loud one. Common readback errors: GL_INVALID_OPERATION (no framebuffer bound / read buffer mismatch), GL_INVALID_ENUM (BGRA+UnsignedByte unsupported by the driver), GL_INVALID_VALUE.

Source

Thrown at src/AddIns/Uno.WinUI.Graphics3DGL/GLCanvasElement.cs:558

				if (_readbackAsRgbaWithSwap)
				{
					_gl.ReadPixels(0, 0, (uint)RenderSize.Width, (uint)RenderSize.Height, GLEnum.Rgba, GLEnum.UnsignedByte, (void*)ptr);
					SwapRedBlue((byte*)ptr, (int)RenderSize.Width * (int)RenderSize.Height);
				}
				else
				{
					_gl.ReadPixels(0, 0, (uint)RenderSize.Width, (uint)RenderSize.Height, GLEnum.Bgra, GLEnum.UnsignedByte, (void*)ptr);
				}
			});
			_backBuffer.PixelBuffer.Length = (uint)RenderSize.Width * (uint)RenderSize.Height * BytesPerPixel;
#endif

			// glReadPixels doesn't throw on failure (e.g. an unsupported readback format); it only
			// sets a GL error and leaves the destination buffer untouched, which would show up as
			// a permanently blank canvas.
			if (_gl.GetError() is var readbackError && readbackError is not GLEnum.NoError)
			{
				throw new InvalidOperationException(
					$"The framebuffer readback failed with {readbackError} while copying the framebuffer to the back buffer.");
			}

			_backBuffer.Invalidate();
		}
		catch (Exception e)
		{
			if (this.Log().IsEnabled(LogLevel.Error))
			{
				this.Log().Error($"{nameof(GLCanvasElement)} rendering failed. The element will no longer render.", e);
			}

			IsGLInitialized = false;
		}
	}

	// Decides the readback format once, while the context is current and the element's framebuffer
	// is bound (see OnLoaded). Any GLES context can lack BGRA read support, and extension strings

View on GitHub (pinned to 0418340488)

Solutions

  1. Confirm the driver supports GL_BGRA_EXT for readback; on GLES fall back to GL_RGBA + byte swizzle.
  2. Ensure the offscreen framebuffer is still bound and complete immediately before ReadPixels (re-check status).
  3. Catch InvalidOperationException around the render pass and stop further rendering of this element (the surrounding code already logs and disables rendering).

Example fix

// before
_gl.ReadPixels(0, 0, w, h, GLEnum.Bgra, GLEnum.UnsignedByte, (void*)ptr);
if (_gl.GetError() is var e && e is not GLEnum.NoError)
{
	throw new InvalidOperationException($"readback failed with {e}");
}

// after (graceful degrade)
_gl.ReadPixels(0, 0, w, h, GLEnum.Bgra, GLEnum.UnsignedByte, (void*)ptr);
if (_gl.GetError() is var e && e is not GLEnum.NoError)
{
	this.Log().Error($"readback failed with {e}; element will render blank.");
	return;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify framebuffer still complete right before readback
if (_gl.CheckFramebufferStatus(GLEnum.Framebuffer) != GLEnum.FramebufferComplete) return;

Try / catch

try { Readback(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("readback")) { this.Log().Error($"readback disabled: {ex}"); }

Prevention

When it happens

Trigger: The driver rejects the glReadPixels format/type pair (BGRA + UnsignedByte), the read framebuffer is not complete at readback time, or no read framebuffer is bound — and ReadPixels sets GL_INVALID_ENUM/OPERATION.

Common situations: Running on a GL ES or older desktop GL driver without GL_BGRA pixel support; framebuffer invalidated between [49]'s check and readback; context made not-current on the render thread.

Related errors


AI-assisted analysis of unoplatform/uno@0418340488 (2026-08-13). Data as JSON: /api/errors/aa91de9d0f50614b. Report an issue: GitHub.