What does BrokenPipeError mean when a Python script writes to a pipe whose reader has exited?
answer
- An error about the other end
- Nothing is left to read it
- The downstream program exited first
- errno 32, EPIPE, write side only
- A ConnectionError subclass under OSError
basics
~20 sBrokenPipeError says a write failed because nobody is reading the other end any more: the downstream process exited or closed its read descriptor. The kernel fails the write with errno.EPIPE (32) and Python turns that into this OSError subclass.
solid answer
~40 sIt is the exception CPython raises when a write to a pipe or a socket fails with `errno.EPIPE` — the read end is gone, so the bytes can never be consumed. The classic trigger is a script whose stdout is piped into a command that exits early, such as a reader that only wants the first few lines. `BrokenPipeError` is a direct subclass of `ConnectionError`, which subclasses `OSError`, so `except OSError` catches it too. One thing surprises people: because stdout is block-buffered when it is a pipe, the traceback usually points at a `print` far past the one that logically "filled" the pipe — the failing write is a buffer flush, not that statement. It is a write-side failure; the read side of a closed pipe sees end of file instead.
code
pycon · 5 lines>>> import errno
>>> BrokenPipeError.__mro__[:4]
(<class 'BrokenPipeError'>, <class 'ConnectionError'>, <class 'OSError'>, <class 'Exception'>)
>>> errno.EPIPE
32go deeper
Be ready to say in one sentence that the write failed because nothing is reading the other end, and to name the trigger: a script piped into a command that exits early.
Explain the mechanics: the kernel returning errno.EPIPE, the mapping onto BrokenPipeError under ConnectionError and OSError, and why buffering makes the reported line misleading.
Show judgement about whether the failure is fatal. For a filter it is normal termination; for a service it is a per-connection debug log. Never let a bare except hide unrelated write failures such as a full disk.
Own the convention across a fleet of tools: which programs are expected to die quietly on a broken pipe, which must checkpoint first, and how that expectation is documented so pipelines built by other teams behave predictably.
## What the error actually reports A pipe has two ends, and the kernel will accept bytes on the write end only while at least one process still holds the read end open. When every reader closes — normally because the downstream program exited — the pipe is *broken*: there is no longer anyone who could ever consume what you are about to write. The `write` system call therefore does not block and does not quietly discard the data. It fails, and the error number it fails with is `EPIPE`, which is 32 on Linux and macOS. CPython inspects that errno and raises the exception mapped to it: `BrokenPipeError`, carrying the message `[Errno 32] Broken pipe`. The same errno arrives from sockets. Writing to a TCP connection whose peer has fully closed usually gives `ConnectionResetError` on the first attempt and `BrokenPipeError` on the next, because the peer's reset has by then torn the connection down locally. ## Where it sits in the exception hierarchy Since Python 3.3 the flat family of `IOError`/`socket.error` codes has been split into named `OSError` subclasses. `BrokenPipeError` is a direct subclass of `ConnectionError`; `ConnectionError` is a subclass of `OSError`. Practically: - `except BrokenPipeError` catches only this failure. - `except ConnectionError` also catches `ConnectionResetError`, `ConnectionAbortedError` and `ConnectionRefusedError`. - `except OSError` catches all of those plus every other operating-system error, which is usually too wide for a `pass`. The instance still carries the numeric code, so `err.errno == errno.EPIPE` remains a valid check; you rarely need it now that the class encodes the same information. ## The two situations that produce it The first is a producer in a shell pipeline. A script that streams many lines into a reader which stops after a handful — a pager the user quits, a filter that only needs the head of the stream — will keep writing into a pipe that no longer has a reader, and the next flush fails. The second is network code. A request handler that writes a large response to a client that disconnected mid-transfer sees exactly this exception. Here it is routine, not exceptional: on a busy server clients vanish all the time, and the correct handling is to log it at debug level and drop that connection, not to let it propagate. ## Why the traceback points at the wrong line When stdout is a terminal, Python's text layer is line-buffered. When it is a pipe, the layer underneath collects roughly 8 KB before issuing a syscall. So most `print` calls never touch the pipe at all. The one that raises is simply the one unlucky enough to trigger the flush that hits the dead reader, and it can be thousands of lines after the reader actually went away. Treat the reported line as "where the buffer happened to spill", not as the cause. It also means a program can finish its loop with data still buffered and only discover the broken pipe when the interpreter flushes the stream at exit. ## What it is not It is not an end-of-file condition. The reader of a closed pipe gets a zero-length read, which higher-level code surfaces as an empty string or `EOFError`; the *writer* of a pipe with no readers gets `EPIPE`. Confusing the two leads to except clauses that never fire. It is also not a Python-specific fault: every Unix program writing into a dead pipe hits the same kernel condition. What differs is Python's reaction to it, which is to raise rather than to let the process be killed. ## Handling it Decide whether the failure is fatal for this program. For a small filter, a broken pipe is normal and the right response is to stop writing and exit quietly. For a long-running service it is a per-connection event to be logged and swallowed. What is never right is a bare `except: pass` wrapped around the whole program, which hides genuine write failures such as a full disk. Catch the specific class, and catch it around the streaming loop rather than around each individual write.
- Does the process reading from a closed pipe get BrokenPipeError too?No. The failure is asymmetric. A reader whose writers have all closed simply sees end of file: the read returns zero bytes, which surfaces as an empty `bytes` or `str` from a stream read, or as `EOFError` from a protocol layer that was expecting more. Only the writing side gets `errno.EPIPE` and therefore `BrokenPipeError`.
- Why does the traceback blame a print statement thousands of lines after the reader exited?Because stdout to a pipe is block-buffered. Most `print` calls only copy into an in-process buffer; the syscall happens when that buffer spills. The statement in the traceback is the one that triggered the failing flush, not the first one written after the reader vanished. Treat it as a location, not a cause.
It is like posting letters through a slot in a door after the house has been demolished: the post office does not hold them for you, it hands them straight back marked undeliverable.
saying these in an interview costs you the question
- Thinks BrokenPipeError means the downstream program crashed with a bug
- Confuses it with EOFError, which is the read side of a closed pipe
- Believes it only happens with subprocesses, never with sockets
- Assumes the traceback line is where the reader actually went away
- Wraps the whole program in a bare except to make it disappear