{"record":{"id":"cf0c96761cb3638b","repo":"gofiber/fiber","slug":"sse-write-heartbeat-w","errorCode":null,"errorMessage":"sse: write heartbeat: %w","messagePattern":"sse: write heartbeat: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/sse/event.go","lineNumber":76,"sourceCode":"\t}\n\tif event.Retry > 0 {\n\t\tappendField(&frame, \"retry\", utils.FormatInt(event.Retry.Milliseconds()))\n\t}\n\tif data.hasData {\n\t\tappendData(&frame, data.data)\n\t}\n\tframe.WriteByte('\\n') //nolint:errcheck // bytes.Buffer writes never fail.\n\tif _, err := w.Write(frame.Bytes()); err != nil {\n\t\treturn fmt.Errorf(\"sse: write event: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc writeComment(w *bufio.Writer, comment string) error {\n\tcomment = sanitizeComment(comment)\n\tif comment == \"\" {\n\t\tif _, err := w.WriteString(\":\\n\\n\"); err != nil {\n\t\t\treturn fmt.Errorf(\"sse: write heartbeat: %w\", err)\n\t\t}\n\t\treturn nil\n\t}\n\tfor line := range strings.SplitSeq(comment, \"\\n\") {\n\t\tif _, err := fmt.Fprintf(w, \": %s\\n\", line); err != nil {\n\t\t\treturn fmt.Errorf(\"sse: write comment: %w\", err)\n\t\t}\n\t}\n\tif _, err := w.WriteString(\"\\n\"); err != nil {\n\t\treturn fmt.Errorf(\"sse: finish comment: %w\", err)\n\t}\n\treturn nil\n}\n\ntype eventPayload struct {\n\tdata    string\n\thasData bool\n}","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/sse/event.go#L58-L94","documentation":"Thrown by writeComment when writing the heartbeat frame (\":\\n\\n\") to the writer fails. The heartbeat is sent to keep the connection alive; a write failure means the underlying connection is broken, identical in cause to 208 but on the heartbeat/comment path.","triggerScenarios":"The client has disconnected, a proxy reset the connection, or any network write error occurred while emitting the heartbeat or an empty comment.","commonSituations":"Idle SSE connections dropped by intermediaries between heartbeats; clients closing the browser; mobile network interruptions.","solutions":["Treat the error as end-of-stream; stop the heartbeat loop and return.","Verify HeartbeatInterval is shorter than your proxy's idle timeout.","Do not swallow and retry; a failed write means the stream is dead."],"exampleFix":"// before\nif err := stream.Comment(\"\"); err != nil { log.Error(err) } // keeps trying\n// after\nif err := stream.Comment(\"\"); err != nil {\n    return // connection is gone\n}","handlingStrategy":"try-catch","validationCode":"// Heartbeats are infrastructure; nothing to pre-validate.\n// Just make sure the interval beats your proxy timeout:\ncfg := sse.Config{\n    HeartbeatInterval: 10 * time.Second, // < proxy idle timeout\n    DisableHeartbeat:  false,\n}","typeGuard":null,"tryCatchPattern":"if err := stream.Comment(\"\"); err != nil {\n    return // heartbeat write failed => client/proxy dropped the connection\n}","preventionTips":["Keep DisableHeartbeat=false on long streams.","Set HeartbeatInterval below the smallest idle timeout in your proxy chain.","Treat heartbeat-write errors as terminal; do not continue."],"tags":["sse","network","write","heartbeat","disconnected"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}