gorilla/websocket · critical
repeated read on failed websocket connection
Error message
repeated read on failed websocket connection
What it means
To help developers who ignore read errors, the library counts consecutive reads on a connection that already returned an error; when readErrCount reaches 1000 it panics with this message. ReadMessage keeps returning the same stored error forever after failure, so a tight loop without error handling would otherwise spin indefinitely on a dead connection.
Source
Thrown at conn.go:1041
break
}
if frameType == TextMessage || frameType == BinaryMessage {
c.messageReader = &messageReader{c}
c.reader = c.messageReader
if c.readDecompress {
c.reader = c.newDecompressionReader(c.reader)
}
return frameType, c.reader, nil
}
}
// Applications that do handle the error returned from this method spin in
// tight loop on connection failure. To help application developers detect
// this error, panic on repeated reads to the failed connection.
c.readErrCount++
if c.readErrCount >= 1000 {
panic("repeated read on failed websocket connection")
}
return noFrame, nil, c.readErr
}
type messageReader struct{ c *Conn }
func (r *messageReader) Read(b []byte) (int, error) {
c := r.c
if c.messageReader != r {
return 0, io.EOF
}
for c.readErr == nil {
if c.readRemaining > 0 {
if int64(len(b)) > c.readRemaining {
b = b[:c.readRemaining]View on GitHub (pinned to e064f32e36)
Solutions
- Break out of the read loop as soon as ReadMessage returns an error and close the connection
- Always check and handle the read error; trigger reconnect logic outside the loop
- Ensure only one read goroutine exists per connection and that it exits on first error
- Optionally recover from this panic in the goroutine to prevent process crash, then clean up
Example fix
// before
for {
_, msg, _ := conn.ReadMessage()
process(msg)
}
// after
for {
_, msg, err := conn.ReadMessage()
if err != nil {
conn.Close()
break
}
process(msg)
} Defensive patterns
Strategy: try-catch
Try / catch
for {
_, msg, err := conn.ReadMessage()
if err != nil {
conn.Close()
go reconnect()
return
}
process(msg)
} Prevention
- Always check ReadMessage's error and break the loop on failure
- One read goroutine per connection that exits on first error
- Close the Conn immediately after a read error
- Never write a read loop that discards the returned error
When it happens
Trigger: Calling ReadMessage (or Reader.Read) in a loop ~1000+ times on a connection whose read already failed, typically ignoring the returned error and never closing the Conn.
Common situations: Read loops written as `for { _, _, _ = conn.ReadMessage() }` without checking err; goroutine leaks where a dead connection's reader is never shut down; hot loops that don't break on error.
Related errors
- concurrent write to websocket connection
- websocket: bad handshake
- websocket: invalid compression negotiation
- malformed ws or wss URL
- websocket: duplicate header not allowed:
AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31).
Data as JSON: /api/errors/3148657003daa461.
Report an issue: GitHub.