dotnet/wpf · error · ObjectDisposedException
SR.StreamObjectDisposed
Error message
SR.StreamObjectDisposed
What it means
ObjectDisposedException (message SR.StreamObjectDisposed) thrown by VersionedStream.CheckDisposed when a public Stream API (Length, Position, Flush, Seek, SetLength, Read) is used after the underlying stream was closed (its _stream field set to null). It is the standard 'use after close' guard for versioned compound-file streams.
Solutions
- Read all needed data before the owning package/CompoundFile is disposed
- Reopen the compound file/package to obtain a fresh stream instead of reusing the closed one
- Restructure code so stream lifetime matches usage scope (keep the owning object alive while the stream is referenced)
- Guard accesses with an IsDisposed/can-read check when the stream may already be closed
Example fix
// before: using a stream after the package was disposed
Package package;
using (package = Package.Open(path))
{
partStream = part.GetStream();
}
long len = partStream.Length; // ObjectDisposedException
// after: consume the stream inside the owning package's lifetime
using (var package = Package.Open(path))
{
using (var partStream = part.GetStream())
{
long len = partStream.Length; // OK
ProcessStream(partStream);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
static bool IsUsable(Stream s) => s != null && s.CanRead && s.CanSeek; // CanRead/CanSeek are false after disposal on most streams
Type guard
bool IsAliveStream(VersionedStream vs) => vs != null && vs.CanRead;
Try / catch
try
{
long len = versionedStream.Length;
}
catch (ObjectDisposedException)
{
// reopen the owning package/stream and retry once
using (var fresh = ReopenStream())
{
len = fresh.Length;
}
} Prevention
- Scope stream usage inside the owning package's using block
- Never store package streams in long-lived fields or closures
- Read all needed bytes eagerly if the package's lifetime is uncertain
- Enable dispose-pattern analyzers to catch use-after-dispose
When it happens
Trigger: Calling any read/seek/flush member of a VersionedStream after Close()/Dispose() was called on it — e.g. keeping a reference to the stream after the CompoundFile/package was disposed, or finishing a using block and then reading Position.
Common situations: Disposing a package in a using block but retaining stream references in fields or closures; deferred/lazy reads (e.g. in async code or event handlers) that run after disposal; double-dispose followed by an accidental access.
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
- EncryptedPackageEnvelope object was disposed.
- SR.ByteRangeDownloaderDisposed
- SR.StorageBasedPackagePropertiesDiposed
- SR.StorageRootDisposed
- SR.StreamObjectDisposed
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/44821797a66b2479.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/VersionedStream.cs:256
{
_stream.Close();
}
}
finally
{
_stream = null;
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 (_stream == null)
throw new ObjectDisposedException(null, SR.StreamObjectDisposed);
}
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
private VersionedStreamOwner _versionOwner;
private Stream _stream; // null indicates Disposed state
}
}
View on GitHub (pinned to 81131a70a4)