dotnet/wpf · critical · InvalidOperationException
SR.PackageControllerStreamCorruption
Error message
SR.PackageControllerStreamCorruption
What it means
PackageController.SaveCommit throws InvalidOperationException with SR.PackageControllerStreamCorruption when the source and destination streams are the same object (detected via equal hash codes), which would corrupt the package, and also when the copy failed (source and destination lengths differ). It is a safety check protecting package integrity during commit.
Solutions
- Ensure the save destination is a different stream than the source; open a new FileStream/StreamProxy for the target.
- Check that the destination stream supports writing and was fully written; compare lengths before commit.
- If a copier failed, fix the underlying IO error (permissions, disk space, stream capabilities) and retry the save.
- Dispose/reopen the document cleanly instead of committing from a partially copied package.
Example fix
// before StreamProxy dest = new StreamProxy(sourceStream); // same stream as source packageController.SaveCommit(doc); // after var dest = new StreamProxy(new FileStream(targetPath, FileMode.Create, FileAccess.Write)); packageController.SaveCommit(doc);
Defensive patterns
Strategy: validation
Validate before calling
bool safeToCommit = !ReferenceEquals(doc.Source, doc.Destination) && doc.Source.Length == doc.Destination.Length;
Try / catch
try { packageController.SaveCommit(doc); }
catch (InvalidOperationException) { /* reopen document; do not commit partial copy */ } Prevention
- Never pass the same stream as both source and save destination.
- Verify the destination stream supports writing and was fully copied before commit.
- Resolve underlying IO errors (space, permissions) before retrying saves.
When it happens
Trigger: Saving a document where doc.Destination is a StreamProxy wrapping the same underlying stream as doc.Source; or where no registered copier succeeded so Destination.Length != Source.Length at commit time.
Common situations: Programming mistakes where save target and source reference the same stream; stream types unsupported by any copier causing a truncated/incomplete copy; an earlier IO error leaving the destination partially written.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- SR.DocumentStreamMustBeFileSource
- SR.DocumentStreamMustBeTemporary
- Image_OriginalStreamReadOnly
- ReachPackaging_SigningDoesNotMeetPolicy
- SR.Format(SR.CanOnlyHaveOneChild, typeof(PageContent)…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/23752204ba669a9a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationUI/MS/Internal/Documents/Application/PackageController.cs:123
bool IDocumentController.SaveCommit(Document document)
{
PackageDocument doc = (PackageDocument)document;
if (doc.Destination == null)
{
return false;
}
if (doc.Package.IsDirty || !doc.IsDestinationIdenticalToSource)
{
StreamProxy source = doc.Source as StreamProxy;
StreamProxy destination = doc.Destination as StreamProxy;
// this will catch the case where our source stream is our destination
// stream, which would cause corruption of the package
if (source.GetHashCode() == destination.GetHashCode())
{
throw new InvalidOperationException(
SR.PackageControllerStreamCorruption);
}
// this will catch the case where no one was able to copy the stream
// thus the lengths will not match
if (doc.Source.Length != doc.Destination.Length)
{
throw new InvalidOperationException(
SR.PackageControllerStreamCorruption);
}
// Flush the package to ensure that the relationship parts are
// written out before changes are merged
doc.Package.Flush();
doc.Package.MergeChanges(doc.Destination);
Trace.SafeWrite(Trace.File, "Destination Merged.");
}View on GitHub (pinned to 81131a70a4)