gofiber/fiber · error
sse: write event
Error message
sse: write event: %w
What it means
Thrown by writeEvent when writing the fully-assembled SSE frame to the bufio.Writer (the HTTP response body writer) fails. The %w wraps the underlying network write error. By the time this fires, the frame itself is valid; the problem is the transport — almost always a disconnected or reset client connection.
Solutions
- Treat this as a normal stream-termination: stop writing and return; do not retry writes on a broken stream.
- After the handler returns, consult stream.Err() / the stream context to decide cleanup.
- Ensure your reverse proxy/idle timeouts are longer than your heartbeat interval so intermediaries don't kill idle streams.
- Use the heartbeat (DisableHeartbeat=false) so dead connections are detected proactively.
Example fix
// before
for { if err := stream.Event(ev); err != nil { log.Error(err); continue } }
// after
done := stream.Context().Done()
for {
select {
case <-done:
return // client gone, stop cleanly
default:
if err := stream.Event(ev); err != nil { return }
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// There is nothing to validate locally; the failure is a dead socket.
// Instead, structure the handler to respect the stream lifecycle:
func streamHandler(c fiber.Ctx, s *sse.Stream) error {
ctx := s.Context()
for {
select {
case <-ctx.Done():
return nil // client gone
case ev := <-events:
if err := s.Event(ev); err != nil { return nil }
}
}
} Try / catch
if err := stream.Event(ev); err != nil {
// write failed => connection is gone; stop producing events
return
} Prevention
- Always check the stream context (Context().Done()) and stop when it cancels.
- Set HeartbeatInterval shorter than your reverse-proxy idle timeout.
- Do not retry a failed write — the stream is closed by failLocked.
- Log stream.Err() in OnClose to distinguish clean vs error teardown.
When it happens
Trigger: The client has closed the connection (tab closed, navigation away), an intermediary proxy/load-balancer timed out and reset the stream, a mobile network dropped, or a broken-pipe/EOF occurred during the write.
Common situations: Long-lived SSE streams where the client goes away silently; reverse proxies with short idle timeouts; flaky mobile networks; clients that hard-close without sending a close frame.
Related errors
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/c0f7942c2f2e78b8.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/sse/event.go:67
}
if event.Name != "" {
name, err := sanitizeField(event.Name)
if err != nil {
return fmt.Errorf("sse: invalid event: %w", err)
}
if name != "" {
appendField(&frame, "event", name)
}
}
if event.Retry > 0 {
appendField(&frame, "retry", utils.FormatInt(event.Retry.Milliseconds()))
}
if data.hasData {
appendData(&frame, data.data)
}
frame.WriteByte('\n') //nolint:errcheck // bytes.Buffer writes never fail.
if _, err := w.Write(frame.Bytes()); err != nil {
return fmt.Errorf("sse: write event: %w", err)
}
return nil
}
func writeComment(w *bufio.Writer, comment string) error {
comment = sanitizeComment(comment)
if comment == "" {
if _, err := w.WriteString(":\n\n"); err != nil {
return fmt.Errorf("sse: write heartbeat: %w", err)
}
return nil
}
for line := range strings.SplitSeq(comment, "\n") {
if _, err := fmt.Fprintf(w, ": %s\n", line); err != nil {
return fmt.Errorf("sse: write comment: %w", err)
}
}
if _, err := w.WriteString("\n"); err != nil {View on GitHub (pinned to a105acad6c)