A pipe (or socket) has two ends; this error means your program wrote to its end after the other end had been closed by its owner. The famous case is shell plumbing: python report.py | head prints a few lines, head exits satisfied, and the producer's next write lands in a pipe with no reader. The network case is identical in shape: the client disconnected, and the server's next send discovers it.
# at the shell:
# python3 names.py | head -n 2
for name in load_a_million_names():
print(name) # head exits after 2 lines;
# the next print has no reader
BrokenPipeError: [Errno 32] Broken pipe
# often followed at exit by:
# Exception ignored in: <_io.TextIOWrapper ...>
head, less (quit early), or another consumer that closes early, the BrokenPipeError is the correct description of events. Pipelines are supposed to work this way; only the noise needs handling.from signal import signal, SIGPIPE, SIG_DFL; signal(SIGPIPE, SIG_DFL) near startup. The process then dies quietly by the signal, like standard Unix tools, instead of printing a traceback.os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno())) so the interpreter's exit-time flush does not raise the “Exception ignored” follow-up.close() in a finally, a context manager exiting) and then written by another part reproduces this without any external reader; there, it is a real bug in ordering, and the traceback's write site is the place to fix.If writes fail with broken pipe immediately, even with a live reader, the reader process died for its own reasons (crash, killed, failed to start in a subprocess pipeline). Inspect the consumer: subprocess pipelines where the child's stderr was never drained are a recurring cause, since a full stderr buffer can kill or block the child while the parent keeps writing.
Why is there a second error, 'Exception ignored', after the first? Because the interpreter flushes standard streams during shutdown, and the flush hits the same broken pipe, raising inside cleanup code where it can only be reported as ignored. Redirecting stdout to devnull in your handler (step three) silences it properly.
Does BrokenPipeError mean data was lost? It means the remaining data had nowhere to go: the reader had already stopped reading, by its own choice or death. What the reader consumed before closing arrived intact; everything after the close was never going to be read, which for head-style consumers is the intended behavior.