gofiber/fiber · error

sse: Handler must not be nil

Error message

sse: Handler must not be nil

What it means

sse.New constructs a Server-Sent Events handler. ConfigDefault leaves Handler nil by design (there is no sensible default stream implementation), so the caller must supply a Config.Handler that writes events to the Stream. New panics when cfg.Handler is nil because there would be nothing to execute on the stream — returning a handler that does nothing would be a silent bug.

Solutions

  1. Provide a Handler when constructing the config: sse.New(sse.Config{Handler: func(c fiber.Ctx, s *sse.Stream) error { ... }}).
  2. If you build the Config incrementally, assert cfg.Handler != nil before calling sse.New in tests.

Example fix

// before
app.Get("/events", sse.New()) // panics: no Handler

// after
app.Get("/events", sse.New(sse.Config{
    Handler: func(c fiber.Ctx, s *sse.Stream) error {
        return s.WriteEvent("ping", "hello")
    },
}))
Defensive patterns

Strategy: validation

Validate before calling

func newSSE(cfg sse.Config) fiber.Handler {
    if cfg.Handler == nil {
        log.Fatal("sse: Config.Handler must be set")
    }
    return sse.New(cfg)
}

Type guard

func hasSSEHandler(cfg sse.Config) bool { return cfg.Handler != nil }

Prevention

When it happens

Trigger: sse.New(sse.Config{}) with no Handler field, or sse.New() followed by code that expected a default — there is no default Handler.

Common situations: Building an SSE endpoint and forgetting to wire the Handler; refactoring that moved the streaming logic into a separate function but never assigned it to Config.Handler; copying a Config struct where Handler was left nil.

Related errors


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

Appendix: source

Thrown at middleware/sse/sse.go:27

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/utils/v2"
)

var errStreamClosed = errors.New("sse: stream closed")

// New creates a new SSE handler.
func New(config ...Config) fiber.Handler {
	cfg := configDefault(config...)
	if cfg.Handler == nil {
		panic("sse: Handler must not be nil")
	}

	return func(c fiber.Ctx) error {
		c.Set(fiber.HeaderContentType, fiber.MIMETextEventStream)
		c.Set(fiber.HeaderCacheControl, "no-cache")
		c.Set(fiber.HeaderConnection, "keep-alive")
		c.Set("X-Accel-Buffering", "no")

		streamContext := c.Context()
		lastEventID := c.Get(fiber.HeaderLastEventID)

		c.Abandon()

		return c.SendStreamWriter(func(w *bufio.Writer) {
			stream := newStream(streamContext, w, lastEventID, c.App().Config().JSONEncoder)
			var streamErr error
			defer func() {
				if cfg.OnClose != nil {

View on GitHub (pinned to a105acad6c)