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
- Keep the stream alive within the scope that uses it (move the using block outward).
- Check or track disposal state before use, or re-open the stream when needed.
- Do not use the stream after the owning Package/Part is closed; reopen the part instead.
- 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
- Keep the stream lifetime at least as long as all usages (scope the using block outward)
- Never cache the stream beyond its owning Package's lifetime
- Reopen the part instead of using it after close
- Ensure background work completes before disposal
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
- SR.StreamObjectDisposed
- ArgumentOutOfRangeException(nameof(offset))
- BaseStream
- can’t seek on baseStream
- Current stream object was closed and disposed. Cannot…
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)