2025, Dec 10 15:00

Why pip list | head Triggers BrokenPipeError: Understanding stdout vs stderr and Broken Pipes in Bash/Python

Seeing BrokenPipeError after pip list | head? Learn how stdout and stderr work, why broken pipes are expected, and how to capture clean output safely in Python.

When you peek at package lists in a Bash terminal with something like a quick head on pip’s output, it’s easy to be surprised by a dramatic-looking error. The good news: nothing is actually broken. The behavior is expected once you understand how pipes, stdout, and stderr interact.

Reproducing the symptom

Consider a simple check of the first few installed Python packages:

pip list | head -n 5

You might see output like this:

Package            Version
------------------ ----------------
attrs              21.2.0
Automat            20.2.0
Babel              2.8.0
ERROR: Pipe to stdout was broken
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pipe

What actually happens

The head utility stops reading after it outputs the requested number of lines. That’s its whole job. When the upstream program—here pip—keeps writing more, there’s no longer a reader on the other end of the pipe. The operating system reports this to the writer as a broken pipe. From the writer’s perspective, this is an error because it cannot finish sending the rest of its data.

When the writer is Python, the interpreter surfaces this condition as BrokenPipeError. A Python program can catch it, but often the appropriate response is to let it propagate and exit. That’s exactly what happens here.

a “broken pipe”…is an error from the perspective of the program [writing data] because it prevents the program from completing writing its output.

Why the error text appears separately

Unix-like systems provide two distinct output channels. Standard output is for regular data; standard error is for diagnostics. Interactive terminals display both by default, but they are independent streams under the hood and can be captured or redirected independently. The Python interpreter writes error messages to standard error. So the list of packages you asked for is on standard output, while the BrokenPipeError text is on standard error.

So…is this a problem?

Not in the sense of a bug you need to fix. The behavior is normal and expected whenever a consumer like head intentionally stops early. The important point is that the diagnostic text is not mixed into the standard output stream you’re actually interested in.

Practical takeaway for programmatic capture

If you plan to consume this data programmatically, read standard output for the data and standard error for diagnostics. Keep the streams separate. That way you’ll collect only the package lines you requested without any error text. This separation is precisely why the two streams exist.

Why you shouldn’t blindly suppress it

It can be tempting to silence these messages across the board. That’s risky. If a pipe breaks for a reason you didn’t intend—say you’re piping into another tool and the destination can’t accept more data—then being unable to fully write output should be treated as a real error. Ignoring it would mask legitimate failures.

Code example and unchanged logic

The minimal shell pipeline that triggers the behavior stays the same; the semantics don’t change:

pip list | head -n 5

There’s no need to modify this command to “fix” anything. The toolchain is behaving correctly. If you later wire this into automation, ensure your capture mechanism separates standard output from standard error and consumes only the former for parsing.

Why this matters

Understanding how pipes and the two output streams work prevents false alarms and brittle workarounds. You get clean data handling, predictable automation, and diagnostics that remain visible when they signal genuine issues.

Bottom line

Seeing BrokenPipeError after piping pip list through head is expected. Head closes the pipe after the requested lines; Python reports the write failure on standard error. Treat stdout as your dataset and stderr as diagnostics. Don’t blanket-suppress errors—you want real failures to surface when they matter.