{"record":{"id":"33d1bc192bac04c4","repo":"dotnet/wpf","slug":"sr-packagingcircularreference-writeableondemandstream","errorCode":null,"errorMessage":"SR.PackagingCircularReference","messagePattern":"SR\\.PackagingCircularReference","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationUI/MS/Internal/Documents/Application/WriteableOnDemandStream.cs","lineNumber":293,"sourceCode":"    {\n        if (!_wantedWrite)\n        {\n            throw new NotSupportedException(\n                SR.PackagingWriteNotSupported);\n        }\n\n        if (!_isActiveWriteable)\n        {\n            Stream writer = _writeableStreamFactory(_mode, _access);\n            if (writer == null)\n            {\n                throw new IOException(\n                    SR.PackagingWriteableDelegateGaveNullStream);\n            }\n\n            if (writer.Equals(this))\n            {\n                throw new IOException(\n                    SR.PackagingCircularReference);\n            }\n\n            writer.Position = _active.Position;\n\n            _active = writer;\n            _isActiveWriteable = true;\n        }\n    }\n    #endregion Private Methods\n\n    #region Private Fields\n    //--------------------------------------------------------------------------\n    // Private Fields\n    //--------------------------------------------------------------------------\n\n    /// <summary>\n    /// The delegate we were given to have a writeable stream created.","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationUI/MS/Internal/Documents/Application/WriteableOnDemandStream.cs#L275-L311","documentation":"WriteableOnDemandStream.EnsureWritable rejects a writer stream that equals the on-demand stream itself (writer.Equals(this)); redirecting the active stream to itself would recurse forever. IOException(PackagingCircularReference) is thrown.","triggerScenarios":"Write/SetLength on a WriteableOnDemandStream whose factory delegate returns the same stream instance it manages, instead of an independent underlying stream.","commonSituations":"Factory implementation that caches and returns 'this' or a stream that wraps nothing, typically a copy-paste bug in custom package part implementations.","solutions":["Make the factory return a fresh/independent underlying Stream, never the WriteableOnDemandStream instance itself.","Review stream caching logic so cached streams are the raw storage streams, not the wrapper.","Add a debug assert in the factory comparing the returned stream against the wrapper."],"exampleFix":"// before\nStream Factory(FileMode m, FileAccess a) => this; // circular\n// after\nStream Factory(FileMode m, FileAccess a) => _part.GetStream(m, a);","handlingStrategy":"validation","validationCode":"Stream writer = CreateWriterStream();\nif (ReferenceEquals(writer, onDemandStream))\n    throw new InvalidOperationException(\"Factory returned the on-demand stream itself.\");","typeGuard":"bool IsIndependent(Stream requested, Stream returned) => !ReferenceEquals(returned, requested);","tryCatchPattern":"try { stream.Write(data, 0, data.Length); }\ncatch (IOException ex) when (ex.Message.Contains(\"CircularReference\"))\n{ Log.Error(\"Stream factory returned wrapper stream\", ex); }","preventionTips":["Return raw storage streams from the factory, never the wrapper.","Cache only underlying streams.","Add asserts against self-return."],"tags":["wpf","packaging","circular-reference","io"],"backgroundTag":"internal-invariant-violation","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}