Skip to content

Undocumented breaking change in .NET 10: System.IO.Packaging.Package disposal causes "Package object was closed and disposed" error with DocumentFormat.OpenXml #126465

@damianabb

Description

@damianabb

Description

FYI, this is a summary generated by copilot, I'll try to copy/paste only the relevant information:

Summary
After upgrading from .NET 8 to .NET 10, code using DocumentFormat.OpenXml (version 3.5.1, previously version: 3.3.0) with System.IO.Packaging.Package throws InvalidOperationException: "Package object was closed and disposed, so cannot carry out operations on this object or any stream opened on a part of this package." during document disposal.
This appears to be an undocumented breaking change in .NET 10's disposal behavior that is not listed in the official breaking changes documentation: https://learn.microsoft.com/en-us/dotnet/core/compatibility/10

Environment
• .NET Version: .NET 10.0
• OS: Windows
• Visual Studio: 2026 (18.5.0-insiders)
• DocumentFormat.OpenXml: 3.5.1
• Previous working version: .NET 8 with \OpenXml library v: 3.3.0

Reproduction Steps

Code that worked in .NET 8:

public byte[] Export(string fileTemplate, string fileName, Func<string, string> Replace)
{
    string filePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Data/");
    var fileTemp = filePath + fileTemplate;
    using var stream = new MemoryStream(System.IO.File.ReadAllBytes(fileTemp), true);
    using var wordDoc = WordprocessingDocument.Open(stream, true);
    string docText = "";
    if (wordDoc != null && wordDoc.MainDocumentPart != null)
    {
        XmlDocument doc = new XmlDocument();
        var s = wordDoc.MainDocumentPart.GetStream(FileMode.OpenOrCreate, FileAccess.ReadWrite);
        doc.Load(s);
        docText = doc.OuterXml;
        s.Seek(0, SeekOrigin.Begin);
        docText = Replace.Invoke(docText);
        doc.LoadXml(docText);
        doc.Save(s);
        wordDoc.Save();
    }            
    return stream.ToArray();
}

Expected behavior

Code should work as it did in .NET 8, with the document being saved and the byte array returned successfully.

Actual behavior

Exception thrown during wordDoc.Dispose():

System.InvalidOperationException: Package object was closed and disposed, so cannot carry out operations on this object or any stream opened on a part of this package.
   at System.IO.Packaging.Package.ThrowIfObjectDisposed()
   at System.IO.Packaging.Package.get_FileOpenAccess()
   at DocumentFormat.OpenXml.Features.PackageFeatureBase.DocumentFormat.OpenXml.Packaging.IPackage.get_FileOpenAccess()
   at DocumentFormat.OpenXml.Packaging.Builder.DelegatingPackageFeature.get_FileOpenAccess()
   at DocumentFormat.OpenXml.Packaging.Builder.DelegatingPackageFeature.get_FileOpenAccess()
   at DocumentFormat.OpenXml.Packaging.OpenXmlPackage.SavePartContents(Boolean save)
   at DocumentFormat.OpenXml.Packaging.OpenXmlPackage.Dispose(Boolean disposing)

Regression?

Yes, it worked on .NET 8, OpenXML 3.3.0

Known Workarounds

Root Cause Analysis
The disposal behavior of System.IO.Packaging.Package appears to have changed in .NET 10. When using using var for both the MemoryStream and WordprocessingDocument, the underlying Package is disposed before the WordprocessingDocument can complete its save operation during disposal.

Workaround:

public byte[] Export(string fileTemplate, string fileName, Func<string, string> Replace)
{
    string filePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Data/");
    var fileTemp = filePath + fileTemplate;

    using var inputStream = new MemoryStream(System.IO.File.ReadAllBytes(fileTemp));
    using var outputStream = new MemoryStream();
    
    using (var sourceDoc = WordprocessingDocument.Open(inputStream, false))
    {
        using var destDoc = (WordprocessingDocument)sourceDoc.Clone(outputStream, true);
        
        if (destDoc?.MainDocumentPart != null)
        {
            string docText;
            XmlDocument doc = new XmlDocument();
            
            var s = destDoc.MainDocumentPart.GetStream(FileMode.OpenOrCreate, FileAccess.ReadWrite);
            doc.Load(s);
            docText = doc.OuterXml;
            
            docText = Replace.Invoke(docText);
            
            s.Seek(0, SeekOrigin.Begin);
            doc.LoadXml(docText);
            doc.Save(s);
        }
        
        destDoc.Save();
    }

    return outputStream.ToArray();
}

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions