{"record":{"id":"950224e92f8e2f34","repo":"dotnet/wpf","slug":"streams-for-exposure-as-ilockbytes-must-be-seekable","errorCode":null,"errorMessage":"Streams for exposure as ILockBytes must be seekable.","messagePattern":"Streams for exposure as ILockBytes must be seekable\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/PrivateUnsafeNativeCompoundFileMethods.cs","lineNumber":71,"sourceCode":"            int grfMode,       // Specifies the access mode for opening the storage object\n            int stgfmt,        // Specifies the storage file format, 5 is DocFile\n            int grfAttrs,      // Reserved; must be zero\n            IntPtr pStgOptions,// Pointer to STGOPTIONS, not marshalled, must use NULL.\n            IntPtr reserved2,  // Reserved; must be null\n            ref Guid riid,     // Specifies the GUID of the interface pointer\n            out UnsafeNativeIStorage ppObjectOpen       //Pointer to an interface pointer\n            );    \n\n        [DllImport(\"ole32.dll\")]\n        internal static extern int PropVariantClear(ref PROPVARIANT pvar);\n\n        internal class UnsafeLockBytesOnStream : UnsafeNativeILockBytes, IDisposable\n        {\n            internal UnsafeLockBytesOnStream( Stream underlyingStream )\n            {\n                if( !underlyingStream.CanSeek )\n                {\n                    throw new NotSupportedException(\n                        SR.ILockBytesStreamMustSeek);\n                }\n\n                _baseStream = underlyingStream;\n            }\n            \n            public void Dispose()\n            {              \n                Dispose(true);\n                GC.SuppressFinalize(this);\n            }\n\n            /// <summary>\n            /// Dispose(bool)\n            /// </summary>\n            /// <param name=\"disposing\"></param>\n            protected virtual void Dispose(bool disposing)\n            {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/PrivateUnsafeNativeCompoundFileMethods.cs#L53-L89","documentation":"UnsafeLockBytesOnStream wraps a Stream to expose it as an ILockBytes for native compound-file APIs. The underlying stream must support seeking (random access); a non-seekable stream (e.g. network stream, pipe, or a forward-only stream) causes NotSupportedException with the 'Streams for exposure as ILockBytes must be seekable' message.","triggerScenarios":"Passing a non-seekable Stream (CanSeek == false) — such as an unbuffered network stream, stdin/stdout wrapper, or decompression stream — into UnsafeLockBytesOnStream / UnsafeNativeCompoundFileCommon.CreateOnStream.","commonSituations":"Opening a Package/ZipPackage directly over an HTTP response stream or crypto stream without buffering it into a MemoryStream or FileStream first.","solutions":["Wrap the non-seekable stream in a seekable buffer: copy it into a MemoryStream or a temp FileStream before creating the compound file.","Verify stream.CanSeek before constructing, and fail fast with a clear error if false.","For long content, spool to disk (FileStream) rather than holding it all in memory."],"exampleFix":"// before\nvar package = Package.Open(nonSeekableNetworkStream);\n// after\nusing (var buffered = new MemoryStream()) {\n    nonSeekableNetworkStream.CopyTo(buffered);\n    buffered.Position = 0;\n    var package = Package.Open(buffered);\n}","handlingStrategy":"validation","validationCode":"if (!underlyingStream.CanSeek) throw new ArgumentException(nameof(underlyingStream), \"stream must be seekable to back ILockBytes\");","typeGuard":null,"tryCatchPattern":"try { CreateOnStream(stream); }\ncatch (NotSupportedException ex) { /* buffer into MemoryStream/FileStream and retry */ }","preventionTips":["Check stream.CanSeek before any compound-file/Package API","Buffer network or crypto streams into MemoryStream/FileStream first","Prefer FileStream or MemoryStream sources for compound files"],"tags":["stream","seekable","not-supported","packaging"],"backgroundTag":"unsupported-operation","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}