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

  1. Ensure all reads/writes complete before Dispose — await async work inside the using block.
  2. Lengthen the stream's lifetime (return the buffer contents via ToArray/GetBuffer instead of the stream).
  3. Check CanRead before use and treat false as disposed; guard with ObjectDisposedException handling.
  4. 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

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


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)