AvaloniaUI/Avalonia · error · InvalidOperationException
Visual was invalidated during the render pass
Error message
Visual was invalidated during the render pass
What it means
Thrown by CompositingRenderer.AddDirty when a visual is marked dirty while the renderer is mid-render (_updating is true). Mutating the dirty set during the render pass would corrupt the iteration/update, so it is treated as an illegal re-entrancy (InvalidOperationException).
Source
Thrown at src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs:88
/// <inheritdoc/>
public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
private void QueueUpdate()
{
if(_queuedUpdate)
return;
_queuedUpdate = true;
_compositor.RequestCompositionUpdate(_update);
}
/// <inheritdoc/>
public void AddDirty(Visual visual)
{
if (_isDisposed)
return;
if (_updating)
throw new InvalidOperationException("Visual was invalidated during the render pass");
_dirty.Add(visual);
QueueUpdate();
}
/// <inheritdoc/>
public IEnumerable<Visual> HitTest(Point p, Visual? root, Func<Visual, bool>? filter)
{
using var _ = Diagnostic.PerformingHitTest();
CompositionVisual? rootVisual = null;
if (root != null)
{
if (root.CompositionVisual == null)
yield break;
rootVisual = root.CompositionVisual;
}
Func<CompositionVisual, bool>? f = null;View on GitHub (pinned to 11c5427268)
Solutions
- Defer the invalidation: dispatch it after the render pass with Dispatcher.UIThread.Post(() => visual.InvalidateVisual()).
- Do not mutate visuals or call InvalidateVisual from inside Render/OnRender — compute state from the current draw context instead.
- If invalidation is driven by a timer or async completion, ensure it is posted, not invoked inline.
Example fix
// before
public override void Render(DrawingContext ctx)
{
Draw(ctx);
InvalidateVisual(); // throws during render pass
}
// after
public override void Render(DrawingContext ctx)
{
Draw(ctx);
Dispatcher.UIThread.Post(InvalidateVisual); Defensive patterns
Strategy: try-catch
Try / catch
try { visual.InvalidateVisual(); }
catch (InvalidOperationException) when (rendering) { Dispatcher.UIThread.Post(() => visual.InvalidateVisual()); } Prevention
- Never call InvalidateVisual/AddDirty from inside Render/OnRender — post it instead.
- Use Dispatcher.UIThread.Post to defer invalidation from render-time handlers.
- Keep render callbacks pure: draw only, no tree mutation.
When it happens
Trigger: Calling visual.InvalidateVisual() (or anything that calls AddDirty) from within a render callback, a RenderOverride, or a property changed handler that fires synchronously during the composition update pass.
Common situations: A custom rendered control invalidates itself inside its own Render/OnRender; a property change during layout triggers an invalidate that runs during the update; background work marshals an invalidate onto the render thread at the wrong time.
Related errors
- Object is not yet attached to the compositor
- This API is only available from OnRender
- Animation has no key frames
- Invalid animation duration
- CompositionImportedGpuImage
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/5cb16803f2c52bc7.
Report an issue: GitHub.