{"record":{"id":"ea2dbe0ab6021390","repo":"dotnet/wpf","slug":"cannot-access-the-stream-after-it-is-closed","errorCode":null,"errorMessage":"Cannot access the stream after it is closed.","messagePattern":"Cannot access the stream after it is closed\\.","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/IO/Packaging/DeobfuscatingStream.cs","lineNumber":269,"sourceCode":"        #region Private\n        //------------------------------------------------------\n        //\n        //  Private Properties\n        //\n        //------------------------------------------------------\n        //------------------------------------------------------\n        //\n        //  Private Methods\n        //\n        //------------------------------------------------------\n        /// <summary>\n        /// Call this before accepting any public API call (except some Stream calls that\n        /// are allowed to respond even when Closed\n        /// </summary>\n        private void CheckDisposed()\n        {\n            if (_obfuscatedStream == null)\n                throw new ObjectDisposedException(null, SR.Media_StreamClosed);\n        }\n\n        /// <summary>\n        /// Apply de-obfuscation as necessary (the first 32 bytes of the underlying stream are obfuscated\n        /// </summary>\n        private void Deobfuscate(byte[] buffer, int offset, int count, long readPosition)\n        {\n            // only the first 32 bytes of the underlying stream are obfuscated\n            //   if the read position is beyond offset 32, there is no need to do de-obfuscation\n            if (readPosition >= ObfuscatedLength || count <= 0)\n                return;\n\n            // Find out how many bytes in the buffer are needed to be XORed\n            // Note on casting:\n            //      count can be safely cast to long since it is int\n            //      We don't need to check for overflow of (readPosition + (long) count) since count is int and readPosition is less than\n            //          ObfuscatedLength\n            //      The result of (Math.Min(ObfuscatedLength, readPosition + (long) count) - readPosition) can be safely cast to int","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/IO/Packaging/DeobfuscatingStream.cs#L251-L287","documentation":"DeobfuscatingStream keeps an underlying _obfuscatedStream; when the wrapper is disposed, the field is set to null and every subsequent Stream API call (Read, Write, Seek, SetLength, Flush, Position) routes through CheckDisposed, which throws ObjectDisposedException. This library throws it because the deobfuscation wrapper no longer has a usable underlying stream after Close/Dispose.","triggerScenarios":"Calling Read/Write/Seek/SetLength/Flush or reading/writing the Position property after calling Dispose() (or Close()) on the DeobfuscatingStream instance.","commonSituations":"Caching the stream in a field and reusing it after a 'using' block ended; disposing in one code path while a background reader still holds a reference; double-dispose followed by read in exception cleanup paths.","solutions":["Re-open the XPS part (e.g. via PackWebRequest/PackagePart.GetStream) to obtain a fresh DeobfuscatingStream instead of reusing the disposed one","Restructure code so all reads happen inside the using block that owns the stream","Track disposal state in the owning class and skip/refresh operations on disposed streams"],"exampleFix":"// before\nDeobfuscatingStream s = CreateFontStream();\ns.Dispose();\nvar b = new byte[32]; s.Read(b, 0, 32); // throws\n// after\nusing (DeobfuscatingStream s = CreateFontStream())\n{\n    var b = new byte[32];\n    s.Read(b, 0, 32);\n}","handlingStrategy":"try-catch","validationCode":"if (stream == null) throw new InvalidOperationException(\"Stream not initialized\");\n// track disposal explicitly:\nbool disposed = _disposed; // expose via property if wrapping","typeGuard":"static bool IsUsable(DeobfuscatingStream s) => s != null && !disposedSet.Contains(s); // track in a HashSet on Dispose","tryCatchPattern":"try { stream.Read(buf, 0, buf.Length); } catch (ObjectDisposedException) { stream = CreateFontStream(); stream.Read(buf, 0, buf.Length); }","preventionTips":["Keep all stream access inside the owning using block","Never store streams in long-lived fields; reopen on demand","Set references to null after Dispose so stale use is caught early"],"tags":["xps","packaging","disposed","stream-lifecycle"],"backgroundTag":"invalid-state-transition","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"}