valyala/fasthttp · warning

fasthttputil: connection closed

Error message

fasthttputil: connection closed

What it means

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.

Source

Thrown at fasthttputil/pipeconns.go:224

		case <-c.pc.stopCh:
			// rCh may contain data when stopCh is closed.
			// Read the data before returning EOF.
			select {
			case c.b = <-c.rCh:
			default:
				return io.EOF
			}
		}
	}

	c.bb = c.b.b
	return nil
}

var errWouldBlock = errors.New("would block")

// ErrConnectionClosed indicates that the underlying connection is closed. It could mean that the client has disconnected.
var ErrConnectionClosed = errors.New("fasthttputil: connection closed")

type timeoutError struct{}

func (e *timeoutError) Error() string {
	return "fasthttputil: timeout"
}

// Timeout implements the Timeout method of the net.Error interface.
// This allows for checks like:
//
//	if x, ok := err.(interface{ Timeout() bool }); ok && x.Timeout() {
func (e *timeoutError) Timeout() bool {
	return true
}

// ErrTimeout is returned from Read() or Write() on timeout.
var ErrTimeout = &timeoutError{}

View on GitHub (pinned to c96f600972)

Solutions

  1. Treat ErrConnectionClosed as EOF-equivalent in read loops and stop reading/writing
  2. Always Close the PipeConn on both sides when done to release blocked goroutines
  3. Retry with a fresh connection if the exchange must complete
  4. Avoid concurrent unsynchronized use of a single PipeConn

Example fix

// before
for {
    n, err := conn.Read(buf)
    if err != nil { log.Fatal(err) }
    process(buf[:n])
}
// after
for {
    n, err := conn.Read(buf)
    if err != nil {
        if err == fasthttputil.ErrConnectionClosed {
            return // peer disconnected, normal EOF path
        }
        log.Fatal(err)
    }
    process(buf[:n])
}
Defensive patterns

Strategy: try-catch

Type guard

func isConnClosed(err error) bool {
    return errors.Is(err, fasthttputil.ErrConnectionClosed)
}

Try / catch

n, err := pc.Read(buf)
if errors.Is(err, fasthttputil.ErrConnectionClosed) {
    return io.EOF // treat as normal peer disconnect
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/ab5cb0823cf044db. Report an issue: GitHub.