Skip to content

What is a broken pipe error (SIGPIPE)?

A broken pipe error happens when a program writes to a pipe or socket whose reader has closed, which sends it SIGPIPE and often ends it with exit code 141.

Last updated , 9 min read

What is a broken pipe error (SIGPIPE)?

A broken pipe error is the failure a program gets when it writes into a pipe that has no reader left. The kernel sends the writer the SIGPIPE signal, which ends it by default. If the writer ignores that signal, the write fails instead with the error EPIPE, whose message text is "Broken pipe" on Linux and macOS.

SIGPIPE is the signal a program receives when it writes to a pipe whose reader has closed. The signal(7) manual page lists it as signal 13, with a default action that ends the process. When a signal ends a process, Bash reports 128 plus the signal number, so SIGPIPE becomes exit code 141.

Any command-line application can get the error when its output goes to a reader that stops early, e.g. head -n 5. That stop is normal use, so the program should exit quietly instead of printing an error. The same error appears on a network connection that the other side has closed, e.g. in an SSH session, and there it usually means a real failure.

What causes a broken pipe?

The failure happens in five steps when a reader exits before its writer is done:

  1. The shell starts both programs and connects the writer's stdout to the reader's input through a pipe.
  2. The writer writes into the pipe's buffer in the kernel, and the reader reads from it.
  3. The reader gets what it needs, e.g. 3 lines, and exits, which closes the read end of the pipe.
  4. The writer makes its next write, and the kernel sends it SIGPIPE.
  5. The signal ends the writer, unless the writer ignores or handles it, in which case the write returns EPIPE.
How a broken pipe reaches the writer Writer acme export write Pipe buffer in the kernel read Reader head -n 3 exits after 3 lines read end closed next write Kernel no reader left SIGPIPE default action ends the writer Exit code 141 reported by Bash Write returns EPIPE if SIGPIPE is ignored or handled Error handling BrokenPipeError
The reader's exit breaks the pipe, but the writer finds out only on its next write. What happens then depends on how the writer treats SIGPIPE.

Timing decides whether the writer ever gets the error. The pipe(7) manual page gives a Linux pipe a capacity of 16 pages, which is 64 KiB with 4 KiB pages. A writer whose whole output fits in that buffer can finish before the reader exits. The same command can then pass on a small export and fail on a large one.

A write to a terminal, or TTY, does not raise SIGPIPE, so a command that runs cleanly in a terminal can still fail once its output is piped.

What does a broken pipe look like?

Here is an illustrative example. Acme Co. sells furniture online, and its command-line tool, acme, exports orders. A coding agent rewrote the export in Python to stream rows from the database. A developer at Acme previews the first rows:

  1. The developer runs acme export --format csv | head -n 3.
  2. The head command prints the header and 2 rows, then exits.
  3. Python ignores SIGPIPE, so the export's next write fails with EPIPE, and Python raises BrokenPipeError.
  4. Nothing in acme catches the exception, so Python prints a stack trace to stderr, which is still the terminal.
  5. Python's final flush of stdout fails too, so the interpreter exits with status 120.

The terminal shows this output from Python 3.14, with the traceback shortened:

$ acme export --format csv | head -n 3
order_id,total
A-1042,$240.00
A-1043,$120.00
Traceback (most recent call last):
  File "/opt/acme/export.py", line 41, in export
    sys.stdout.write(row)
BrokenPipeError: [Errno 32] Broken pipe
Exception ignored while flushing sys.stdout:
BrokenPipeError: [Errno 32] Broken pipe
$ echo "${PIPESTATUS[@]}"
120 0

PIPESTATUS is a Bash array with the exit status of each command in the last pipeline, and $? holds only the status of head. A C or Go writer ends silently from SIGPIPE instead, e.g. yes | head -n 1 leaves 141 0 in the array. In GitHub Actions, a step that sets shell: bash runs with pipefail, so a writer that ends on SIGPIPE can fail the step with Error: Process completed with exit code 141.

This example is simplified. A real CLI would need the same handling in each command that writes to stdout, not only in export.

What changes when a coding agent writes the code?

A coding agent often tests a command through a harness that reads all of its output, e.g. subprocess.run with capture_output=True. That reader never closes early, so the path for a closed pipe never runs and the agent's tests pass. A smoke test that checks only the status of acme export | head -n 1 passes as well, because without pipefail Bash reports the status of head.

Agents also pipe their own commands through head to keep tool output short, and the result depends on whether the shell sets pipefail. Without pipefail, a writer that crashed with a traceback still leaves the pipeline a status of 0. With pipefail, a writer that stopped correctly on SIGPIPE turns the command into a failure with 141. Either result can lead the agent to edit code that works, or to report "done" for code that does not.

Reading the diff seldom reveals the bug, because the bug is a missing handler, not a wrong line. Running code with the reader closed before the first write reveals it on each run. That test belongs among the steps to verify a CLI that an agent built.

How do you fix or prevent a broken pipe error?

On stdout, the fix is to stop writing, print nothing, and exit. The fix depends on the language:

  • Python. Because Python ignores SIGPIPE, a closed pipe raises BrokenPipeError instead of ending the program. The Python signal documentation recommends catching it at the entry point, pointing stdout at os.devnull, and exiting with 1. It warns against restoring the default with signal(SIGPIPE, SIG_DFL), because a closed socket connection would then end the program.
  • Node.js. Node.js also ignores SIGPIPE, so an unhandled EPIPE error from process.stdout.write crashes the program with a stack trace. The global console ignores these errors, so a program that prints with console.log keeps running for a reader that is gone. In both cases, a listener for the error event of process.stdout can exit when err.code is "EPIPE".
  • Go and C. A Go program that writes to a broken pipe on stdout or stderr ends quietly on SIGPIPE, as the os/signal documentation states. A C program does the same by default. If it ignores the signal, it has to check each write and stop on EPIPE.
  • Shell. In a shell script that sets pipefail, a writer that ends on SIGPIPE fails the whole pipeline with 141. A writer that exits with 1 on a broken pipe fails it too. A reader that reads to the end, e.g. sed -n 1,5p, lets the writer finish.

In Python, the pattern from the documentation wraps the entry point:

import os
import sys

def main():
    try:
        export_orders()
        sys.stdout.flush()  # raise the error here, inside the try block
    except BrokenPipeError:
        # Point stdout at /dev/null so the flush at exit cannot fail again.
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)

Catch only the broken pipe error, because a handler for any write error also hides a real failure, e.g. a full disk. A quiet exit also stops the work partway, so a command that prints progress to stdout while it writes files can end before its files are written.

A reliable test closes the reader before the program writes, so the result does not depend on the buffer or on timing. The test below checks two implicit oracles, an empty stderr and a process that ends before its timeout, plus the exit code:

import os
import subprocess

def test_export_stops_quietly_when_the_reader_is_gone():
    read_end, write_end = os.pipe()
    os.close(read_end)  # no reader, so the first write fails
    result = subprocess.run(
        ["acme", "export", "--format", "csv"],
        stdout=write_end, stderr=subprocess.PIPE, text=True, timeout=10,
    )
    os.close(write_end)
    assert result.stderr == ""
    assert result.returncode in (1, -13)  # -13 means SIGPIPE ended it

How is SIGPIPE different from EPIPE?

SIGPIPE is a signal that the kernel sends to the writing process, and EPIPE is the error code that the failed write returns. Both come from the same event. The write(2) manual page states that the writer gets EPIPE only if it catches, blocks, or ignores SIGPIPE, because otherwise the signal ends it first.

The same page lists EPIPE for a socket whose reading end is closed, so a write to a closed network connection fails the same way, e.g. in an SSH session. A setting can also move a program from one case to the other. The signal(7) page states that an ignored signal stays ignored when a process starts another program, so a parent that ignores SIGPIPE passes that setting on.

FAQs

What does exit code 141 mean?

Exit code 141 means that a process ended because it received SIGPIPE, which is signal 13, and Bash reports 128 plus the signal number. The usual cause is a writer whose reader closed early, e.g. head. With pipefail set, that code fails the whole pipeline, e.g. in a GitHub Actions step.

Should a CLI print an error on a broken pipe?

A CLI should not print an error when the reader of its standard output closes early, because that early stop, e.g. by head, is normal use. The program should stop writing and exit quietly. A broken pipe on a network connection is different and usually worth reporting.

Can a broken pipe happen on a network socket?

A broken pipe can happen on a network socket when a program writes to a connection that the other side has closed. The write fails with EPIPE, or the program receives SIGPIPE, and tools report the error as a broken pipe, e.g. in an SSH session.

How do you test for a broken pipe?

A test for a broken pipe closes the reader before the program writes, then checks that stderr is empty and that the process ends within a timeout. Piping a small output into head is not a reliable test, because the whole output can fit in the pipe's buffer.