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

  1. Do not use an ImmediateDrawingContext after Dispose(); scope all draws inside its using lifetime.
  2. Track disposal and short-circuit deferred draw operations.
  3. 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

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


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/ad984d9d237f756f. Report an issue: GitHub.