{"record":{"id":"3148657003daa461","repo":"gorilla/websocket","slug":"repeated-read-on-failed-websocket-connection","errorCode":null,"errorMessage":"repeated read on failed websocket connection","messagePattern":"repeated read on failed websocket connection","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"conn.go","lineNumber":1041,"sourceCode":"\t\t\tbreak\n\t\t}\n\n\t\tif frameType == TextMessage || frameType == BinaryMessage {\n\t\t\tc.messageReader = &messageReader{c}\n\t\t\tc.reader = c.messageReader\n\t\t\tif c.readDecompress {\n\t\t\t\tc.reader = c.newDecompressionReader(c.reader)\n\t\t\t}\n\t\t\treturn frameType, c.reader, nil\n\t\t}\n\t}\n\n\t// Applications that do handle the error returned from this method spin in\n\t// tight loop on connection failure. To help application developers detect\n\t// this error, panic on repeated reads to the failed connection.\n\tc.readErrCount++\n\tif c.readErrCount >= 1000 {\n\t\tpanic(\"repeated read on failed websocket connection\")\n\t}\n\n\treturn noFrame, nil, c.readErr\n}\n\ntype messageReader struct{ c *Conn }\n\nfunc (r *messageReader) Read(b []byte) (int, error) {\n\tc := r.c\n\tif c.messageReader != r {\n\t\treturn 0, io.EOF\n\t}\n\n\tfor c.readErr == nil {\n\n\t\tif c.readRemaining > 0 {\n\t\t\tif int64(len(b)) > c.readRemaining {\n\t\t\t\tb = b[:c.readRemaining]","sourceCodeStart":1023,"sourceCodeEnd":1059,"githubUrl":"https://github.com/gorilla/websocket/blob/e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f/conn.go#L1023-L1059","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nfor {\n    _, msg, _ := conn.ReadMessage()\n    process(msg)\n}\n// after\nfor {\n    _, msg, err := conn.ReadMessage()\n    if err != nil {\n        conn.Close()\n        break\n    }\n    process(msg)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"for {\n    _, msg, err := conn.ReadMessage()\n    if err != nil {\n        conn.Close()\n        go reconnect()\n        return\n    }\n    process(msg)\n}","preventionTips":["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"],"tags":["websocket","panic","error-handling","read-loop"],"backgroundTag":"repeated-read-on-failed-connection","analyzedSha":"e064f32e3674d9d79a8fd417b5bc06fa5c6cad8f","analyzedAt":"2026-08-31T12:40:58.222Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}