dotnet/wpf · error · ObjectDisposedException

SR.StreamObjectDisposed

Error message

SR.StreamObjectDisposed

What it means

CompressEmulationStream.CheckDisposed guards every public API (Read, Write, Seek, SetLength, Flush, Position). Once Dispose has run, any further call throws ObjectDisposedException with SR.StreamObjectDisposed, mirroring System.IO.Stream conventions.

Solutions

  1. Keep the stream alive within the scope that uses it (move the using block outward).
  2. Check or track disposal state before use, or re-open the stream when needed.
  3. Do not use the stream after the owning Package/Part is closed; reopen the part instead.
  4. Catch ObjectDisposedException at the boundary if lifetime is inherently racy.

Example fix

// before
Stream s;
using (var es = new CompressEmulationStream(baseStream, temp, t))
{
    s = es;
}
s.Read(buf, 0, buf.Length); // throws
// after
using (var es = new CompressEmulationStream(baseStream, temp, t))
{
    es.Read(buf, 0, buf.Length);
}
Defensive patterns

Strategy: try-catch

Validate before calling

bool SafeCanUse(CompressEmulationStream s) { try { _ = s.Position; return true; } catch (ObjectDisposedException) { return false; } }

Type guard

sealed class GuardedStream
{
    private CompressEmulationStream _s;
    public bool TryUse(Action<CompressEmulationStream> action)
    {
        if (_s == null) return false;
        try { action(_s); return true; }
        catch (ObjectDisposedException) { return false; }
    }
}

Try / catch

try { es.Read(buf, 0, buf.Length); }
catch (ObjectDisposedException) { ReopenAndRetry(); }

Prevention

When it happens

Trigger: Calling Read/Write/Seek/Position/SetLength/Flush on a CompressEmulationStream after Dispose or a closing 'using' block has completed — e.g. caching the stream beyond its scope or using it after the owning Package is closed.

Common situations: Storing the stream in a field and using it after the using-block exits; closing the parent package then reading; background tasks holding the stream past disposal; double-dispose followed by use.

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 dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/906a437d9fd9fdae. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompressEmulationStream.cs:321

                        _disposed = true;
                    }
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        /// <summary>
        /// Call this before accepting any public API call (except some Stream calls that
        /// are allowed to respond even when Closed
        /// </summary>
        protected void CheckDisposed()
        {
            if (_disposed)
                throw new ObjectDisposedException(null, SR.StreamObjectDisposed);
        }

        #region Private
        //------------------------------------------------------
        //
        //  Private Variables
        //
        //------------------------------------------------------
        private bool    _disposed;          // disposed?
        private bool    _dirty;             // do we need to recompress?
        protected Stream  _baseStream;      // stream we ultimately decompress from and to in the container
        protected Stream _tempStream;       // temporary storage for the uncompressed stream
        private IDeflateTransform _transformer;   // does the actual compress/decompress for us
        #endregion
    }
}

View on GitHub (pinned to 81131a70a4)