dotnet/wpf · error · IOException

Current stream object was closed and disposed. Cannot…

Error message

Current stream object was closed and disposed. Cannot access a closed stream.

What it means

ManagedIStream.Stat reports stream attributes by probing CanRead/CanWrite. A stream that is neither readable nor writable is treated as closed, so it throws IOException with the StreamObjectDisposed message — deliberately IOException (not ObjectDisposedException) because the interop marshaller does not map ObjectDisposedException back to COM HRESULTs correctly.

Solutions

  1. Keep the underlying managed stream open for as long as the IStream wrapper is used by native code.
  2. Check stream.CanRead || stream.CanWrite before calling Stat and reopen/re-create the stream if false.
  3. Catch IOException and treat it as a disposed stream: re-open the source and rebuild the ManagedIStream.

Example fix

// before
pIStream.Stat(out stat, 0); // stream already closed
// after
if (stream.CanRead || stream.CanWrite)
    pIStream.Stat(out stat, 0);
else
    stream = File.OpenRead(path); // re-open before retrying
Defensive patterns

Strategy: try-catch

Validate before calling

bool streamAlive = stream != null && (stream.CanRead || stream.CanWrite);

Type guard

static bool IsOpen(Stream s) => s != null && (s.CanRead || s.CanWrite);

Try / catch

try { pIStream.Stat(out stat, 0); } catch (IOException) when (!stream.CanRead && !stream.CanWrite) { /* stream disposed: re-open and rebuild IStream */ }

Prevention

When it happens

Trigger: Calling IStream.Stat (or ManagedIStream.Stat directly) on a Stream that has been closed/disposed, so both CanRead and CanWrite are false.

Common situations: COM clients holding an IStream reference after the managed stream was disposed; disposing a Package/FileStream while unmanaged code still holds the IStream wrapper; finalizer ordering closing the stream before Stat is called.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/ManagedIStream.cs:136

            };
            if (_ioStream.CanRead && _ioStream.CanWrite)
            {
                streamStats.grfMode |= NativeMethods.STGM_READWRITE;
            }
            else if (_ioStream.CanRead)
            {
                streamStats.grfMode |= NativeMethods.STGM_READ;
            }
            else if (_ioStream.CanWrite)
            {
                streamStats.grfMode |= NativeMethods.STGM_WRITE;
            }
            else
            {
                // A stream that is neither readable nor writable is a closed stream.
                // Note the use of an exception that is known to the interop marshaller
                // (unlike ObjectDisposedException).
                throw new IOException(SR.StreamObjectDisposed);
            }
        }

        /// <summary>
        /// Write at most bufferSize bytes from buffer.
        /// </summary>
        void IStream.Write(Byte[] buffer, Int32 bufferSize, IntPtr bytesWrittenPtr)
        {
            _ioStream.Write(buffer, 0, bufferSize);
            if (bytesWrittenPtr != IntPtr.Zero)
            {
                // If fewer than bufferSize bytes had been written, an exception would
                // have been thrown, so it can be assumed we wrote bufferSize bytes.
                Marshal.WriteInt32(bytesWrittenPtr, bufferSize);
            }
        }

        #region Unimplemented methods

View on GitHub (pinned to 81131a70a4)