Skip to content

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

@jozkee

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.

Metadata

Metadata

Assignees

Labels

area-System.IObugin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions