dotnet/wpf · error · IOException

Read

Error message

Read

What it means

UnsafeIndexingFilterStream.Read reads through an unmanaged IStream (_oleStream) exposed by a COM IFilter. If the underlying OLE stream throws COMException, or a nested IOException occurs, the stream restores Position to the pre-read value and rethrows as a bare IOException with message "Read" and the original exception as InnerException. The real cause is always in InnerException.

Solutions

  1. Inspect the InnerException (COMException) of the IOException for the actual HRESULT and failure reason.
  2. Validate/repair the package file before opening the indexing filter stream.
  3. Retry reading after the stream auto-rewinds Position, if the source is intermittently failing.
  4. Catch IOException and fall back to reading the part via System.IO.Packaging APIs instead of the filter stream.

Example fix

// before
int n = filterStream.Read(buffer, 0, buffer.Length);
// after
try
{
    int n = filterStream.Read(buffer, 0, buffer.Length);
}
catch (IOException ex) when (ex.InnerException is COMException cex)
{
    // inspect cex.ErrorCode for the real HRESULT
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before reading
if (filterStream == null || !filterStream.CanRead)
    throw new InvalidOperationException("Filter stream not readable");

Try / catch

try { int n = filterStream.Read(buffer, 0, count); }
catch (IOException ex)
{
    // Position was auto-restored; log ex.InnerException (often COMException with HRESULT)
    throw new IOException("Indexing filter read failed", ex);
}

Prevention

When it happens

Trigger: Calling Stream.Read on an UnsafeIndexingFilterStream when the underlying _oleStream.Read COM call fails (corrupt or non-seekable source stream, filter error) or an inner IOException propagates.

Common situations: Indexing/searching a damaged or partially downloaded XPS/OPC package; the filter's stream fails mid-read; callers that inspect only ex.Message see the useless string "Read" and miss the COMException details.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/770aef0d968edd79. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/UnsafeIndexingFilterStream.cs:110

            // Prepare location of return value and call the COM object.
            int    bytesRead;
            IntPtr pBytesRead = new IntPtr(&bytesRead);

            // Prepare to restore position in case the read fails.
            long positionBeforeReadAttempt = this.Position;

            try 
            {
                // Pin the array wrt GC while using an address in it.
                fixed (byte *bufferPointer = &buffer[offset])
                {
                    _oleStream.Read(new IntPtr(bufferPointer), count, pBytesRead);
                }
            }
            catch (COMException comException)
            {
                this.Position = positionBeforeReadAttempt;
                throw new IOException("Read", comException);
            }
            catch (IOException ioException)
            {
                this.Position = positionBeforeReadAttempt;
                throw new IOException("Read", ioException);
            }
            return bytesRead;
        }

        /// <summary>
        /// Seek -unmanaged streams do not allow seeking beyond the end of the stream
        /// and since we rely on the underlying stream to validate and return the seek
        /// results, unlike managed streams where seeking beyond the end of the stream
        /// is allowed we will get an exception.
        /// </summary>
        /// <param name="offset">Offset in byte.</param>
        /// <param name="origin">Offset origin (start, current, or end).</param>
        public override unsafe long Seek(long offset, SeekOrigin origin)

View on GitHub (pinned to 81131a70a4)