Skip to content

[Breaking Change] StreamWriter keeps working after being disposed if leaveOpen: true is used #89646

Description

@jozkee

In below snippet I was expecting an exception to be thrown but instead the StreamWriter keeps working:

var ms = new MemoryStream();
var sw = new StreamWriter(ms, leaveOpen: true);

sw.Write("Hello");
sw.Dispose();

sw.Write(" World");
sw.Flush();
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray())); // prints "Hello World".

If I change leaveOpen to false, it does throw as I expected.

Originally posted by @jozkee in #89477 (comment)

cc @Neme12 @dersia


[Breaking Change] StreamWriter keeps working after being disposed if leaveOpen: true is used

We are fixing StreamWriter so that it correctly throws ObjectDisposedException after Dispose() / DisposeAsync() when constructed with leaveOpen: true. The fix is currently in PR #125189.

Related:

Goal

The goal is to align StreamWriter disposal behavior with StreamReader and standard IDisposable semantics. When leaveOpen: true, Dispose() should mark the writer as disposed (so subsequent operations throw) while leaving the underlying stream open. Currently, the _disposed flag is never set when leaveOpen: true, which means write and flush calls silently succeed after disposal.

Existing Behavior

When a StreamWriter is constructed with leaveOpen: true and then disposed, subsequent Write, Flush, and async variants continue to work without throwing any exception:

var ms = new MemoryStream();
var sw = new StreamWriter(ms, leaveOpen: true);
sw.Write("Hello");
sw.Dispose();
sw.Write(" World"); // No exception — writes succeed
sw.Flush();
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
// prints "Hello World"

Note: StreamReader with the same leaveOpen: true flag correctly throws ObjectDisposedException after disposal. This inconsistency has existed since the leaveOpen parameter was introduced.

Breaking Change

After this fix, all write and flush operations on a disposed StreamWriter throw ObjectDisposedException, regardless of the leaveOpen parameter:

var ms = new MemoryStream();
var sw = new StreamWriter(ms, leaveOpen: true);
sw.Write("Hello");
sw.Dispose();
sw.Write(" World"); // Now throws ObjectDisposedException

The change is technically breaking because code that relied on the ability to write after disposal with leaveOpen: true will now throw at runtime. However, use-after-dispose is always documented as erroneous behavior in .NET, so we expect the practical impact to be low.

Error: System.ObjectDisposedException: Cannot write to a closed TextWriter.

Workaround: Remove usage patterns that write to a StreamWriter after calling Dispose() or DisposeAsync(). If you need to continue writing to the underlying stream, create a new StreamWriter wrapping it.

Affected APIs: All overloads of StreamWriter.Write, StreamWriter.WriteLine, StreamWriter.WriteAsync, StreamWriter.WriteLineAsync, StreamWriter.Flush, and StreamWriter.FlushAsync.

Feedback

We would love your feedback on this change. If you have scenarios where you depend on the current (pre-fix) behavior, please comment on this issue.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions