dotnet/wpf · error · ObjectDisposedException
SR.StorageRootDisposed
Error message
SR.StorageRootDisposed
What it means
This is an ObjectDisposedException thrown by StorageRoot.CheckRootDisposedStatus when the compound file's root storage has already been disposed (RootDisposed is true). The CompoundFile-based Packaging APIs (StorageRoot, StorageInfo, StreamInfo) invalidate the underlying OLE structured storage when the root is closed/disposed, and every non-static external API re-checks disposal before acting. It signals the developer called a packaging API after the owning CompoundFile/StorageRoot was closed.
Solutions
- Ensure all reads/writes complete before disposing the CompoundFile/Package; keep the owning object alive for the duration of use.
- Re-open the compound file (e.g. StorageRoot.Open / Package.Open) if you need access after disposal instead of reusing the old instance.
- Check RootDisposed (or guard with try/catch ObjectDisposedException) before calling APIs on long-lived references.
- Fix lifetime ownership: don't dispose in one layer while another layer still holds references.
Example fix
// before
var root = StorageRoot.Open(path); // disposed elsewhere/earlier
root.Flush(); // throws ObjectDisposedException
// after
using (var root = StorageRoot.Open(path))
{
// do all work here, before disposal
root.Flush();
} Defensive patterns
Strategy: try-catch
Validate before calling
if (root == null || root.RootDisposed) throw new InvalidOperationException("Compound file root already disposed."); Type guard
bool IsUsable(StorageRoot root) => root != null && !root.RootDisposed;
Try / catch
try { root.Flush(); }
catch (ObjectDisposedException) { /* reopen the compound file and retry the operation */ } Prevention
- Scope the StorageRoot/Package in a using block and do all work inside it.
- Never cache StreamInfo/StorageInfo references beyond the owning file's lifetime.
- Check RootDisposed before API calls on long-lived references.
- Centralize open/close so disposal happens exactly once, after all consumers finish.
When it happens
Trigger: Calling OpenAccess, Flush, or GetDataSpaceManager on a StorageRoot (or any StorageInfo/StreamInfo operation that routes through these) after the root compound file has been disposed/closed, e.g. using a Package or StreamInfo obtained before a 'using' block ended.
Common situations: Using a Package/StorageRoot inside a 'using' or after Close()/Dispose(); caching a StreamInfo/StreamInfo-derived object in a field and using it after the file was closed; closing the package in one method and writing in another.
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.ByteRangeDownloaderDisposed
- Cannot access Stream object because it was closed or…
- EncryptedPackageEnvelope object was disposed.
- File contains data in format version
- FixedPageReader
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/2e4c13845cedc8f5.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/IO/Packaging/CompoundFile/StorageRoot.cs:443
dataSpaceManager = new DataSpaceManager( this );
dataSpaceManagerInitializationInProgress = false;
}
return dataSpaceManager;
}
internal IStorage GetRootIStorage()
{
return rootIStorage;
}
// Check whether this StorageRoot class still has its underlying storage.
// If not, throw an object disposed exception. This should be checked from
// every non-static container external API.
internal void CheckRootDisposedStatus()
{
if( RootDisposed )
throw new ObjectDisposedException(null, SR.StorageRootDisposed);
}
// Check whether this StorageRoot class still has its underlying storage.
internal bool RootDisposed
{
get
{
return ( null == rootIStorage );
}
}
/// <summary>
/// This will create a container StorageRoot based on the given IStorage
/// interface
/// </summary>
/// <param name="root">The new IStorage (RCW) upon which to build the new StorageRoot</param>
/// <returns>New StorageRoot object built on the given IStorage</returns>
private static StorageRoot CreateOnIStorage( IStorage root )View on GitHub (pinned to 81131a70a4)