The broken pipe
is a TCP/IP error occurring when you write to a stream where the other end (the peer) has closed the underlying connection. The first write to the closed connection causes the peer to reply with an RST
packet indicating that the connection should be terminated immediately. The second write to the socket that has already received the RST
causes the broken pipe
error. To detect the broken pipe
in Go, check if the error returned by the peer is equal to syscall.EPIPE
. Usually, this error can be seen when the server crashes while the client is sending data to it.
Reproduce the broken pipe
error
In the following example, we reproduce the broken pipe
error by creating a server and client that do the following:
- the server reads a single byte and then closes the connection
- the client sends three bytes with an interval of one second between them
The server receives the first client byte and closes the connection. The next byte of the client sent to the closed connection causes the server to reply with an RST
packet. The socket that received the RST
will return the broken pipe
error when more bytes are sent to it. This is what happens when the client sends the last byte to the server.
package main
import (
"errors"
"log"
"net"
"os"
"syscall"
"time"
)
func server() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
conn, err := listener.Accept()
if err != nil {
log.Fatal("server", err)
os.Exit(1)
}
data := make([]byte, 1)
if _, err := conn.Read(data); err != nil {
log.Fatal("server", err)
}
conn.Close()
}
func client() {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal("client", err)
}
// write to make the connection closed on the server side
if _, err := conn.Write([]byte("a")); err != nil {
log.Printf("client: %v", err)
}
time.Sleep(1 * time.Second)
// write to generate an RST packet
if _, err := conn.Write([]byte("b")); err != nil {
log.Printf("client: %v", err)
}
time.Sleep(1 * time.Second)
// write to generate the broken pipe error
if _, err := conn.Write([]byte("c")); err != nil {
log.Printf("client: %v", err)
if errors.Is(err, syscall.EPIPE) {
log.Print("This is broken pipe error")
}
}
}
func main() {
go server()
time.Sleep(3 * time.Second) // wait for server to run
client()
}
Output:
2021/10/21 19:10:01 client: write tcp 127.0.0.1:50389->127.0.0.1:8080: write: broken pipe
2021/10/21 19:10:01 This is broken pipe error
Handle the broken pipe
error
To handle the broken pipe
, you need to check if the error returned from the other end of the connection is an instance of syscall.EPIPE
. In the example above, we perform this check using the errors.Is()
function and print the message "This is broken pipe error"
if it occurs. The broken pipe
can be seen on either the client or server side, depending on which one is trying to write to the closed connection. Typically there is no need to handle it in any special way since it is normal that a connection may be interrupted by either side of the communication. For example, you can ignore the error, log it or reconnect when it occurs.
Difference between broken pipe
and connection reset by peer
Usually, you get the broken pipe
error when you write to the connection after the RST
is sent, and when you read from the connection after the RST
instead, you get the connection reset by peer
error. Check our article about connection reset by peer error to better understand the difference between these two errors.