AvaloniaUI/Avalonia · error · ObjectDisposedException

DrawingContext

Error message

DrawingContext

What it means

PushedState.Dispose throws ObjectDisposedException(nameof(DrawingContext)) when _states or _transformContainers is null — i.e. disposing a PushedState whose owning context was already disposed. This is the use-after-dispose guard for the per-push disposable.

Source

Thrown at src/Avalonia.Base/Media/ImmediateDrawingContext.cs:241

            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();
                if (_type == PushedStateType.Matrix)
                    _context.CurrentTransform = _matrix;
                else if (_type == PushedStateType.Clip)
                    _context.PlatformImpl.PopClip();
                else if (_type == PushedStateType.Opacity)
                    _context.PlatformImpl.PopOpacity();
                else if (_type == PushedStateType.GeometryClip)
                    _context.PlatformImpl.PopGeometryClip();
                else if (_type == PushedStateType.OpacityMask)
                    _context.PlatformImpl.PopOpacityMask();
                else if (_type == PushedStateType.MatrixContainer)
                {
                    var cont = _context._transformContainers.Pop();
                    _context._currentContainerTransform = cont.ContainerTransform;

View on GitHub (pinned to 11c5427268)

Solutions

  1. Dispose PushedState values within the context's lifetime (they are returned by using declarations).
  2. Do not retain PushedState structs beyond the draw call.
  3. Ensure the context outlives every push/pop pair.

Example fix

// before - leaked state disposed after context gone
PushedState s;
using (var ctx = ...CreateDrawingContext()) { s = ctx.PushTransform(m); }
s.Dispose(); // ObjectDisposedException

// after
using (var ctx = ...CreateDrawingContext())
using (ctx.PushTransform(m)) { /* draw */ }
Defensive patterns

Strategy: validation

Validate before calling

// Do not dispose a PushedState after its owning context is disposed.
using (var ctx = CreateContext())
using (var s = ctx.PushTransform(m))
{ /* draw */ } // s disposed here, within ctx lifetime

Prevention

When it happens

Trigger: A PushedState disposable outliving its ImmediateDrawingContext: the context's using-block exits (disposing all states), then a previously returned PushedState is disposed again, or a leaked PushedState is disposed after the context was torn down.

Common situations: Storing PushedState structs in fields/closures and disposing them after the render context closed; nested using scopes that leak.

Related errors


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