gofiber/fiber · info

sse: write heartbeat: %w

Error message

sse: write heartbeat: %w

What it means

The SSE heartbeat (an empty comment ':\n\n') could not be written to the response stream. Heartbeats are emitted on a timer by startHeartbeat to keep the connection alive; a failure means the client has disconnected or the connection broke.

Source

Thrown at middleware/sse/event.go:76

	}
	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 {
		return fmt.Errorf("sse: finish comment: %w", err)
	}
	return nil
}

type eventPayload struct {
	data    string
	hasData bool
}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Heartbeat write failures already stop the heartbeat goroutine internally; ensure your handler also observes stream.Done() and exits.
  2. Make heartbeat interval shorter than the proxy idle timeout so the link stays open while the client is alive.
  3. Handle stream closure gracefully in the main handler loop.
Defensive patterns

Strategy: validation

Validate before calling

// choose heartbeat < proxy idle timeout
app.Use(sse.New(sse.Config{
    Handler: sseHandler,
    HeartbeatInterval: 10 * time.Second,
}))

Try / catch

// heartbeat failures are handled internally; observe stream end in your handler.
go func() {
    <-stream.Done()
    cancel() // unblock your producer
}()

Prevention

When it happens

Trigger: The heartbeat ticker fires after the client closed the connection; network drop during an otherwise-idle SSE connection; proxy closed the idle link.

Common situations: Idle SSE connections behind strict proxies; clients that sleep/suspend; mobile network changes.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/cf0c96761cb3638b.json. Report an issue: GitHub.