dotnet/wpf · error · ObjectDisposedException

SR.StreamObjectDisposed

Error message

SR.StreamObjectDisposed

What it means

RightsManagementEncryptedStream.CheckDisposed throws ObjectDisposedException(SR.StreamObjectDisposed) when any stream operation (Length, Position, Flush, Seek, SetLength, Read, Write, Close paths) is attempted after the stream has been closed/disposed (_baseStream == null). The class nulls its base stream on Close, so any subsequent member access is rejected.

Solutions

  1. Do not use the stream after Dispose/Close; scope all access inside a single using block.
  2. Check ObjectDisposedException (or wrap access) and reopen the package/stream if you need to read again.
  3. Audit lifetime ownership: ensure the owner (Package/EncryptedPackageEnvelope) outlives every consumer of the stream.
  4. If you must detect disposal, test stream.CanRead/CanSeek before use (returns false after dispose) rather than calling members that throw.
  5. Fix double-dispose patterns: remove redundant explicit Close when the stream is in a using statement.

Example fix

// before: stream used after disposal
RightsManagementEncryptedStream stream;
using (stream = CreateRmStream())
{
    stream.Read(buf, 0, buf.Length);
}
stream.Seek(0, SeekOrigin.Begin); // ObjectDisposedException

// after: all work inside the using scope
using var stream = CreateRmStream();
stream.Read(buf, 0, buf.Length);
stream.Seek(0, SeekOrigin.Begin);
Defensive patterns

Strategy: type-guard

Validate before calling

// guard every access
if (stream == null || !stream.CanRead)
    throw new InvalidOperationException("The rights-managed stream is closed; reopen the package first.");

Type guard

static bool IsOpen(Stream s) => s != null && s.CanRead && s.CanSeek; // false after Dispose/Close

Try / catch

try
{
    stream.Seek(0, SeekOrigin.Begin);
}
catch (ObjectDisposedException)
{
    // stream was closed; reopen owning package and retry once
    stream = ReopenRmStream();
}

Prevention

When it happens

Trigger: Calling Read/Seek/Length/Position/Flush/SetLength/Write on a RightsManagementEncryptedStream after Close() or Dispose() has been called — e.g. using the stream after a using block ends, or a cached stream reference used post-disposal.

Common situations: Holding a long-lived reference to the encrypted stream while the XpsDocument/Package that owns it was closed; double-dispose via nested using blocks; reading the stream on another thread after the package was disposed; reusing a stream variable after an earlier operation closed it.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/78647fe16e8752bd. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/RightsManagementEncryptedStream.cs:717

            {
                long firstBlockNumber = GetBlockNo(blockSize, start);
                firstBlockOffset = firstBlockNumber * blockSize;
                blockCount = GetBlockSpanCount(blockSize, start, size);

                if (canMergeBlocks)
                {
                    // we need to recalculate everything as if it were a single large block 
                    blockSize = (int)(blockSize * blockCount);
                    blockCount = 1;
                }
            }
        }

        private void CheckDisposed()
        {
            if (_baseStream == null)
            {
                throw new ObjectDisposedException(null, SR.StreamObjectDisposed);
            }
        }

        private void FlushCacheIfNecessary()
        {
            checked
            {
                if (_readCache.MemoryConsumption + _writeCache.MemoryConsumption > _autoFlushHighWaterMark)
                {
                    FlushCache();
                }                
            }
}
        
        private void FlushCache()
        {
            checked
            {

View on GitHub (pinned to 81131a70a4)