dotnet/wpf · error · IOException

SR.CanNotOpenStorage

Error message

SR.CanNotOpenStorage

What it means

StorageInfo.CanOpenStorage calls IStorage::OpenStorage; when the returned native error code is anything other than STG_E_FILENOTFOUND the library throws IOException with SR.CanNotOpenStorage, wrapping a COMException named for IStorage::OpenStorage. STG_E_FILENOTFOUND is deliberately not thrown — instead openSuccess stays false so callers can check existence.

Solutions

  1. Check the exact child storage name (StgOpenStorage names are exact, not case-insensitive in all paths).
  2. Reopen the StorageRoot with the correct FileAccess for the intended operation.
  3. Verify the compound file is not corrupt by opening it in its owning application.
  4. Catch IOException and inspect the inner COMException HResult for the specific STG_E_* code.

Example fix

// before
var child = root.GetStorageInfo("Data"); // may throw IOException
// after
StreamInfo si;
if (root.TryGetStorageInfo("Data", out var st) || root.StorageInfos.Any(s => s.Name == "Data"))
    child = root.GetStorageInfo("Data");
Defensive patterns

Strategy: validation

Validate before calling

bool exists = root.GetStorageInfos().Any(s => s.Name == childName);
if (!exists) return null; // avoid OpenStorage on a bogus name

Prevention

When it happens

Trigger: Opening a named sub-storage whose IStorage::OpenStorage call fails with an error other than STG_E_FILENOTFOUND (access denied, name invalid, storage disposed) via GetStorageInfo/child storage access.

Common situations: Case/name mismatch resolving to invalid name errors, compound file corruption, accessing a storage on a root opened read-only when write-type access is requested.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/IO/Packaging/CompoundFile/StorageInfo.cs:1093

        int nativeCallErrorCode = 0;

        nativeCallErrorCode = core.safeIStorage.OpenStorage(
                nameInternal,
                null,
                (GetStat().grfMode & SafeNativeCompoundFileConstants.STGM_READWRITE_Bits)
                    | SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE,
                IntPtr.Zero,
                0,
                out childCore.safeIStorage );

        if( SafeNativeCompoundFileConstants.S_OK == nativeCallErrorCode )
        {
            openSuccess = true;
        }
        else if( SafeNativeCompoundFileConstants.STG_E_FILENOTFOUND != nativeCallErrorCode )
        {
            // Error is not STG_E_FILENOTFOUND, pass it on.
            throw new IOException(
                SR.CanNotOpenStorage, 
                new COMException( 
                    SR.Format(SR.NamedAPIFailure, "IStorage::OpenStorage"), 
                    nativeCallErrorCode ));
        }
        // STG_E_NOTFOUND - return openSuccess as false 

        return openSuccess;
    }

    /// <summary>
    /// Most of the time internal methods that want to do an internal check 
    /// to see if a storage exists is only interested in proceeding if it does.
    /// If it doesn't, abort with an exception.  This implements the little
    /// shortcut.
    /// </summary>
    private void VerifyExists()
    {

View on GitHub (pinned to 81131a70a4)