dotnet/wpf · error · UnauthorizedAccessException

SR.CanNotDeleteInReadOnly

Error message

SR.CanNotDeleteInReadOnly

What it means

Thrown by StorageInfo.DestroyElement (used by DeleteSubStorage/DeleteStream paths) when the root compound file was opened with FileAccess.Read. Deletion mutates the file, so the library fails fast with UnauthorizedAccessException; other invalid delete scenarios surface later as wrapped COMExceptions.

Solutions

  1. Reopen the root with FileAccess.ReadWrite before performing deletions
  2. Skip deletion logic when Root.OpenAccess == FileAccess.Read (inspect-before-mutate guard)
  3. Copy the file to a writable location and open the copy read-write if the original must remain untouched
  4. Separate read/inspection code paths from mutation code paths so read-only roots never reach DestroyElement

Example fix

// before
var root = StorageRoot.OpenOnFile(path, FileMode.Open, FileAccess.Read);
root.DeleteSubStorage("oldFragments");
// after
var root = StorageRoot.OpenOnFile(path, FileMode.Open, FileAccess.ReadWrite);
root.DeleteSubStorage("oldFragments");
Defensive patterns

Strategy: validation

Validate before calling

if (root.OpenAccess == FileAccess.Read)
    throw new InvalidOperationException("Deletion requires a read-write root");

Try / catch

try { root.DeleteSubStorage(name); }
catch (UnauthorizedAccessException ex) { /* root opened read-only: reopen ReadWrite or skip cleanup */ }

Prevention

When it happens

Trigger: Calling DestroyElement, DeleteSubStorage or Delete (via transform cleanup in WriteTransformDefinitions) on any StorageInfo belonging to a root opened with FileAccess.Read.

Common situations: Reading an XPS/RM package opened from a read-only stream or URI but then running cleanup/rewrite logic on it; tools that open packages read-only for inspection yet try to prune old fragment storages.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

    /// <summary>
    /// Destroys an element and removes the references ued internally.
    /// </summary>
    internal void DestroyElement( string elementNameInternal )
    {
        object deadElementWalking = core.elementInfoCores[ elementNameInternal ];
        // It's an internal error if we try to call this without first
        //  verifying that it is indeed there.
        Debug.Assert( null != deadElementWalking,
            "Caller should have already verified that there's something to delete.");

        // Can't delete if we're in read-only mode.  This catches some but not
        //  all invalid delete scenarios - anything else would come back as a
        //  COMException of some kind that will be caught and wrapped in an
        //  IOException in the try/catch below.
        if( FileAccess.Read == Root.OpenAccess )
        {
            throw new UnauthorizedAccessException(
                SR.CanNotDeleteInReadOnly);
        }

        //Clean out the entry in dataspacemanager for stream transforms
        DataSpaceManager manager = Root.GetDataSpaceManager();
        if( null != manager )
        {
             if( deadElementWalking is StorageInfoCore )
            {
                //if the element getting deleted is a storage, make sure to delete all its children's references.
                string name = ((StorageInfoCore)deadElementWalking).storageName;
                StorageInfo stInfo = new StorageInfo(this, name);
                RemoveSubStorageEntryFromDataSpaceMap(stInfo);
            }
            else if( deadElementWalking is StreamInfoCore )
            {
                //if the element getting deleted is a stream, the container reference should be removed from dataspacemap of dataspace manager.
                manager.RemoveContainerFromDataSpaceMap(new CompoundFileStreamReference( FullNameInternal, elementNameInternal ));

View on GitHub (pinned to 81131a70a4)