# Why writing to closed stream doesn't error

**URL:** <https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301>\
**Category:** General Usage\
**Tags:** question, io\
**Created:** [March 20, 2026, 10:45am UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301 "2026-03-20T10:45:19Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 20, 2026, 10:45am UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/1 "2026-03-20T10:45:19Z")

</div>

I have been tracking down a bug which where a stream as not open when `write` happened. MWE:

```julia
io = open("/tmp/test", "w")
close(io)
print(io, "test") # why no error?

```

Though the docstrings do not _explicitly_ say that this will work even when the stream is closed, I find it surprising behavior, because the behavior stated in the docstring does not happen either. Is this intended? What’s the rationale?

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [March 20, 2026, 11:50am UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/2 "2026-03-20T11:50:03Z")

</div>

What is “the behavior stated in the docstring”?

I couldn’t find any mention of what happens when writing to a closed `IO`:

1. The docstring of `print([io::IO], xs...)` says nothing about this and links to `show`: “`print` falls back to calling the 2-argument `show(io, x)` for each argument `x` in `xs`”.
2. The docstring of `show(io::IO, mime, x)` doesn’t mention writing to a closed `IO` either and says that “the function body calls `write` (or similar) to write that representation of `x` to `io`”.
3. The docstring of `write(io::IO, x)` doesn’t say anything about writing to a closed `io` _either_ and links back to `print`.
4. “No documentation found for public binding `Core.IO`.”
5. `IOStream` doesn’t mention closed streams.
6. The docstring for `close(stream)` doesn’t explain what it means to “Close an I/O stream” and what happens if one tries to interact with a closed stream.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 20, 2026, 12:02pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/3 "2026-03-20T12:02:13Z")

</div>

> [@ForceBru](#):
>
> What is “the behavior stated in the docstring”?

basically, `?print

> Write to io …

the rest are details. Usually, when a docstring says “do x”, it is understood that not doing x is an exception, ie an error.

But I don’t want to language lawyer here, I am wondering if others find it surprising, and whether it should be reported in an issue. Ie I am more interested in a practical discussion than whether arguing about the docstrings.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [March 20, 2026, 12:06pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/4 "2026-03-20T12:06:16Z")

</div>

_Poll ([view on site](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/4))_

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [March 20, 2026, 12:09pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/5 "2026-03-20T12:09:15Z")

</div>

After some investigation:

1. `print(io::IO, s::Union{String,SubString{String}})` simply calls `write` and _always_ returns `nothing`.
2. `write(io::IO, s::Union{String,SubString{String}})` calls `unsafe_write`, which returns an integer (the number of bytes written). So `print` just wastes the returned value of `write`.

In the case of a closed `IO`, `write` fails to write and returns zero:

```Julia
julia> let
        io = open("/tmp/test", "w")
        close(io)
        @which write(io, "test")
       end
write(io::IO, s::Union{SubString{String}, String})
     @ Base strings/io.jl:246

julia> let
        io = open("/tmp/test", "w")
        close(io)
        write(io, "test")
       end
0

```

BTW, writing zero bytes isn’t exactly an error because I guess it’s technically valid to write an empty string, thus correctly writing zero bytes.

`print` seems to literally be `print(io::IO, s::Union{String,SubString{String}}) = (write(io, s); nothing)`, so it never checks what `write` returned.

I think `write(::IO, x)` should somehow indicate an error if the number of bytes written by `unsafe_write` does not equal the number of bytes in `x`.

---

<div class="post-metadata">

**Author:** ![ForceBru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/forcebru/32/21389_2.png) [@ForceBru](https://discourse.julialang.org/u/ForceBru)\
**Post date:** [March 20, 2026, 12:15pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/6 "2026-03-20T12:15:02Z")

</div>

Speaking of the “operations on closed streams should just silently fail” option, writing to a closed `IOBuffer` predictably errors:

```Julia
julia> let
        io = IOBuffer()
        close(io)
        write(io, "test")
       end
ERROR: ArgumentError: ensureroom failed, IOBuffer is not writeable
Stacktrace:
 [1] ensureroom_reallocate(io::IOBuffer, nshort::UInt64)
   @ Base ./iobuffer.jl:618
 [2] ensureroom
   @ ./iobuffer.jl:603 [inlined]
 [3] unsafe_write(to::IOBuffer, p::Ptr{UInt8}, nb::UInt64)
   @ Base ./iobuffer.jl:823
 [4] write(io::IOBuffer, s::String)
   @ Base ./strings/io.jl:246
 [5] top-level scope
   @ REPL[11]:4

```

The error message is clear too: “`IOBuffer` is _not writeable_”.

It could be interesting to investigate what happens when writing to other subtypes of `IO`: do they raise errors? Do they silently fail?

---

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [March 20, 2026, 2:05pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/7 "2026-03-20T14:05:10Z")

</div>

This is a bug in `write`. It should error if it cannot write the full output, not just return zero. The error should then have details on why the write failed or if it was a partial write the error would contain the number of bytes written.

---

<div class="post-metadata">

**Author:** ![pfitzseb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pfitzseb/32/45566_2.png) [@pfitzseb](https://discourse.julialang.org/u/pfitzseb)\
**Post date:** [March 20, 2026, 3:06pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/8 "2026-03-20T15:06:09Z")

</div>

This seems specific to `IOStream`. `Pipe`, `IOBuffer`, `TCPSocket` etc all error when writing to them after they’re `close`d.

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [March 20, 2026, 9:03pm UTC](https://discourse.julialang.org/t/why-writing-to-closed-stream-doesnt-error/136301/9 "2026-03-20T21:03:20Z")

</div>

For close specifically, see [Clarify documentation around `close`, `flush`, and `isopen` by jakobnissen · Pull Request #58020 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/58020) . Writing to a closed stream has no semantics, and closed IOs are in an undefined state (though it is explicitly not undefined behavior).

In general, IO is woefully underdocumented in Julia and seems to function entirely by vibes.
