AvaloniaUI/Avalonia · critical · InvalidOperationException
Cannot dispose while memory is pinned.
Error message
Cannot dispose while memory is pinned.
What it means
UnmanagedFontMemory.Dispose takes a write lock and refuses to free the native allocation while _pinCount > 0. Pinning marks the buffer as in use by native code (GCHandle-style ref count), and freeing it would cause use-after-free in the glyph cache or rasterizer. The guard is checked under a write lock so the check and free are atomic.
Source
Thrown at src/Avalonia.Base/Media/Fonts/UnmanagedFontMemory.cs:338
// Decrement pin count
Interlocked.Decrement(ref _pinCount);
}
public void Dispose()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
// Always use lock for disposal since we don't have a finalizer
_lock.EnterWriteLock();
try
{
if (Volatile.Read(ref _pinCount) > 0)
{
throw new InvalidOperationException("Cannot dispose while memory is pinned.");
}
if (_ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_ptr);
_ptr = IntPtr.Zero;
}
_length = 0;
}
finally
{
_lock.ExitWriteLock();
_lock.Dispose();
}
}
}
}View on GitHub (pinned to 11c5427268)
Solutions
- Stop issuing Dispose on font memory that is shared; let the GlyphTypeface/FontCollection own its lifetime.
- Ensure all consumers of the pinned buffer (renderers, glyph caches) are torn down before disposing the source font.
- If pinning manually, decrement/release the pin (unpin) before calling Dispose.
- Move ownership to a single long-lived cache (e.g. FontManager) so Dispose only fires on application shutdown when nothing else can pin.
Example fix
// before
using (var typeface = new GlyphTypeface(uri))
{
RenderGlyphs(typeface, codepoints); // pins buffer asynchronously
} // Dispose may fire while a render task still holds the pin -> throws
// after
var typeface = _fontCache.GetOrAdd(uri, u => new GlyphTypeface(u));
RenderGlyphs(typeface, codepoints); // cache owns lifetime; never disposed mid-use Defensive patterns
Strategy: validation
Validate before calling
// Caller must guarantee no pinned spans are outstanding before dispose. // Prefer to never call Dispose on shared font memory; let the cache own it.
Try / catch
// Avoid catching: this is a correctness bug. Refactor ownership instead.
// If you must, log and leak rather than risk use-after-free:
// try { mem.Dispose(); } catch (InvalidOperationException ex)
// { Log.FontStillPinned(ex); /* leak intentionally, do NOT retry */ } Prevention
- Give font memory a single long-lived owner (FontManager/Cache).
- Never dispose GlyphTypeface while text runs or renderers reference it.
- Make pinned spans short-lived and complete synchronously before the using-scope exits.
- Audit finalizers for ordering races against pinned memory.
When it happens
Trigger: Disposing a GlyphTypeface while its UnmanagedFontMemory is still referenced by a pinned Span/GCHandle used in active rendering; calling Dispose from a finalizer-ordering race where another thread is mid-blit; explicit Dispose issued before the rendering pipeline has released its pin.
Common situations: Using-statement scoping of a GlyphTypeface that is still cached in a TextRun; multithreaded UI where one thread disposes a shared font and another composes text; incorrect ownership where a transient owner disposes memory owned by a long-lived cache.
Related errors
- Not all states are disposed
- The control isn't currently attached to a toplevel
- Unable to obtain ANativeWindow
- TopLevel.InternalView was not expected to be null.
- SurfaceView.Holder was not expected to be null during Invali
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/fa9cc2f14642cd82.
Report an issue: GitHub.