AvaloniaUI/Avalonia · error · ObjectDisposedException
ImmediateDrawingContext
Error message
ImmediateDrawingContext
What it means
The PushedState constructor throws ObjectDisposedException(nameof(ImmediateDrawingContext)) when context._states is null. _states is returned to the pool and nulled in Dispose(), so any push (PushTransform, PushClip, PushOpacity, etc.) attempted after the context is disposed fails here.
Source
Thrown at src/Avalonia.Base/Media/ImmediateDrawingContext.cs:227
private readonly ImmediateDrawingContext _context;
private readonly Matrix _matrix;
private readonly PushedStateType _type;
public enum PushedStateType
{
None,
Matrix,
Opacity,
Clip,
MatrixContainer,
GeometryClip,
OpacityMask,
}
internal PushedState(ImmediateDrawingContext context, PushedStateType type, Matrix matrix = default)
{
if (context._states is null)
throw new ObjectDisposedException(nameof(ImmediateDrawingContext));
_context = context;
_type = type;
_matrix = matrix;
_level = context._currentLevel += 1;
context._states.Push(this);
}
public void Dispose()
{
if (_type == PushedStateType.None)
return;
if (_context._states is null || _context._transformContainers is null)
throw new ObjectDisposedException(nameof(DrawingContext));
if (_context._currentLevel != _level)
throw new InvalidOperationException("Wrong Push/Pop state order");
_context._currentLevel--;
_context._states.Pop();View on GitHub (pinned to 11c5427268)
Solutions
- Do not use an ImmediateDrawingContext after Dispose(); scope all draws inside its using lifetime.
- Track disposal and short-circuit deferred draw operations.
- Obtain a fresh context per render pass instead of reusing one.
Example fix
// before
using (var ctx = ...CreateDrawingContext()) { /* ... */ }
ctx.PushTransform(m); // ctx already disposed -> throws
// after - keep all drawing inside the using scope
using (var ctx = ...CreateDrawingContext())
{
ctx.PushTransform(m);
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure no Push* call happens after the context is disposed.
if (ctx is null) return;
// Inside the using-scope only:
using (ctx.PushTransform(m)) { /* draw */ } Prevention
- Keep all Push*/draw calls inside the context's using-scope.
- Do not store a context for reuse across render passes.
- Cancel deferred draw operations when the context is disposed.
When it happens
Trigger: Calling any Push* method on an ImmediateDrawingContext whose Dispose() already ran — e.g. using a context from a completed render or storing a context field that was disposed and then drawing later.
Common situations: Reusing a DrawingContext after the using-block exited; drawing from a background thread after the UI context was disposed; deferred draw operations that outlive the context.
Related errors
- DrawingContext
- Wrong Push/Pop state order
- Transform container stack is non-empty
- Unable to obtain ANativeWindow
- TopLevel.InternalView was not expected to be null.
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/ad984d9d237f756f.
Report an issue: GitHub.