SixLabors/ImageSharp · error · ObjectDisposedException
The stream is closed.
Error message
The stream is closed.
What it means
ChunkedMemoryStream.ThrowDisposed throws ObjectDisposedException when a method is used after the stream was disposed; EnsureNotDisposed is called on member access. ChunkedMemoryStream is ImageSharp's internal pooled memory stream, so accessing it after Dispose is a use-after-dispose bug in the caller.
Solutions
- Ensure all reads/writes complete before Dispose — await async work inside the using block.
- Lengthen the stream's lifetime (return the buffer contents via ToArray/GetBuffer instead of the stream).
- Check CanRead before use and treat false as disposed; guard with ObjectDisposedException handling.
- If ownership is pooled/internal, let ImageSharp manage the stream and copy the data out at the documented point.
Example fix
// before ChunkedMemoryStream ms = CreateStream(); ms.Dispose(); var len = ms.Length; // ObjectDisposedException // after using var ms = CreateStream(); var len = ms.Length; byte[] data = ms.ToArray(); // copy out before dispose
Defensive patterns
Strategy: validation
Validate before calling
if (stream is ChunkedMemoryStream cms && !cms.CanRead) throw new InvalidOperationException("Stream already disposed"); Type guard
static bool IsUsable(Stream s) => s is { CanRead: true, CanWrite: false } or { CanRead: true, CanWrite: true }; Try / catch
try { buffer = stream.GetSpan().ToArray(); }
catch (ObjectDisposedException ex) { log.Error(ex, "ChunkedMemoryStream used after dispose"); throw; } Prevention
- Keep 'using' scopes alive across all async operations (await inside using)
- Copy data out (ToArray) before the owning scope disposes
- Never share a disposable stream across threads without lifetime ownership
When it happens
Trigger: Calling Read/Write/Seek/Length/Position on a ChunkedMemoryStream after Dispose() or a 'using' block ended; returning the stream from a method whose using scope already closed it; sharing the stream across threads where one disposes it early.
Common situations: Encoding/decoding callbacks that capture the stream and run after the owning scope disposes it; async code with a 'using' that doesn't await before disposal; caching the stream for later reuse.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Attempted to allocate a buffer of length=
- Attempted to allocate a buffer of length=
- Attempted to allocate a buffer of negative length=
- Cannot read from the stream.
- Cannot read from the stream.
AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13).
Data as JSON: /api/errors/698d93c4dca28ac5.
Report an issue: GitHub.
Appendix: source
Thrown at src/ImageSharp/IO/ChunkedMemoryStream.cs:386
}
this.bufferIndex = currentChunkIndex;
// Safe to cast here as we know the offset is less than the chunk length.
this.chunkIndex = (int)offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EnsureNotDisposed()
{
if (this.isDisposed)
{
ThrowDisposed();
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowDisposed() => throw new ObjectDisposedException(nameof(ChunkedMemoryStream), "The stream is closed.");
private sealed class MemoryChunkBuffer : IDisposable
{
private readonly List<MemoryChunk> memoryChunks = [];
private readonly MemoryAllocator allocator;
private readonly int allocatorCapacity;
private bool isDisposed;
public MemoryChunkBuffer(MemoryAllocator allocator)
{
this.allocatorCapacity = allocator.GetBufferCapacityInBytes();
this.allocator = allocator;
}
public int ChunkCount => this.memoryChunks.Count;
public long Length { get; private set; }
View on GitHub (pinned to 59ce6af6fc)