gofiber/fiber · error

sse: write heartbeat

Error message

sse: write heartbeat: %w

What it means

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.

Solutions

  1. Treat the error as end-of-stream; stop the heartbeat loop and return.
  2. Verify HeartbeatInterval is shorter than your proxy's idle timeout.
  3. Do not swallow and retry; a failed write means the stream is dead.

Example fix

// before
if err := stream.Comment(""); err != nil { log.Error(err) } // keeps trying
// after
if err := stream.Comment(""); err != nil {
    return // connection is gone
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Heartbeats are infrastructure; nothing to pre-validate.
// Just make sure the interval beats your proxy timeout:
cfg := sse.Config{
    HeartbeatInterval: 10 * time.Second, // < proxy idle timeout
    DisableHeartbeat:  false,
}

Try / catch

if err := stream.Comment(""); err != nil {
    return // heartbeat write failed => client/proxy dropped the connection
}

Prevention

When it happens

Trigger: The client has disconnected, a proxy reset the connection, or any network write error occurred while emitting the heartbeat or an empty comment.

Common situations: Idle SSE connections dropped by intermediaries between heartbeats; clients closing the browser; mobile network interruptions.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/cf0c96761cb3638b. Report an issue: GitHub.

Appendix: 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 a105acad6c)