{"record":{"id":"ab5cb0823cf044db","repo":"valyala/fasthttp","slug":"fasthttputil-connection-closed","errorCode":null,"errorMessage":"fasthttputil: connection closed","messagePattern":"fasthttputil: connection closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"fasthttputil/pipeconns.go","lineNumber":224,"sourceCode":"\t\tcase <-c.pc.stopCh:\n\t\t\t// rCh may contain data when stopCh is closed.\n\t\t\t// Read the data before returning EOF.\n\t\t\tselect {\n\t\t\tcase c.b = <-c.rCh:\n\t\t\tdefault:\n\t\t\t\treturn io.EOF\n\t\t\t}\n\t\t}\n\t}\n\n\tc.bb = c.b.b\n\treturn nil\n}\n\nvar errWouldBlock = errors.New(\"would block\")\n\n// ErrConnectionClosed indicates that the underlying connection is closed. It could mean that the client has disconnected.\nvar ErrConnectionClosed = errors.New(\"fasthttputil: connection closed\")\n\ntype timeoutError struct{}\n\nfunc (e *timeoutError) Error() string {\n\treturn \"fasthttputil: timeout\"\n}\n\n// Timeout implements the Timeout method of the net.Error interface.\n// This allows for checks like:\n//\n//\tif x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {\nfunc (e *timeoutError) Timeout() bool {\n\treturn true\n}\n\n// ErrTimeout is returned from Read() or Write() on timeout.\nvar ErrTimeout = &timeoutError{}\n","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/valyala/fasthttp/blob/c96f600972c6f4a7a30d664257b340ebe9d60124/fasthttputil/pipeconns.go#L206-L242","documentation":"ErrConnectionClosed is returned by pipeConn Read/Write (and surfaced via httpConnError) when the in-memory pipe connection is closed — typically the peer hung up. fasthttputil documents it as indicating the underlying connection is closed, e.g. the client has disconnected.","triggerScenarios":"Writing to or reading from a fasthttputil.PipeConn after either endpoint called Close(); the writer side exits so the reader gets ErrConnectionClosed on subsequent reads; using the connection concurrently from multiple goroutines after shutdown.","commonSituations":"In-process client/server tests using InmemoryListener + PipeConn where one side finishes early; long-lived streaming pipes where the peer goroutine exits; forgetting that Read returns ErrConnectionClosed (not io.EOF) when the peer disconnects.","solutions":["Treat ErrConnectionClosed as EOF-equivalent in read loops and stop reading/writing","Always Close the PipeConn on both sides when done to release blocked goroutines","Retry with a fresh connection if the exchange must complete","Avoid concurrent unsynchronized use of a single PipeConn"],"exampleFix":"// before\nfor {\n    n, err := conn.Read(buf)\n    if err != nil { log.Fatal(err) }\n    process(buf[:n])\n}\n// after\nfor {\n    n, err := conn.Read(buf)\n    if err != nil {\n        if err == fasthttputil.ErrConnectionClosed {\n            return // peer disconnected, normal EOF path\n        }\n        log.Fatal(err)\n    }\n    process(buf[:n])\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isConnClosed(err error) bool {\n    return errors.Is(err, fasthttputil.ErrConnectionClosed)\n}","tryCatchPattern":"n, err := pc.Read(buf)\nif errors.Is(err, fasthttputil.ErrConnectionClosed) {\n    return io.EOF // treat as normal peer disconnect\n}","preventionTips":["Treat ErrConnectionClosed as EOF in read loops","Close both pipe ends when finished","Don't share a PipeConn across goroutines unsynchronized","Reconnect with a fresh PipeConn when needed"],"tags":["fasthttputil","pipe","connection-closed","network"],"backgroundTag":"connection-closed","analyzedSha":"c96f600972c6f4a7a30d664257b340ebe9d60124","analyzedAt":"2026-08-31T22:48:28.265Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}