{"record":{"id":"61807293d696957f","repo":"gofiber/fiber","slug":"sse-handler-must-not-be-nil","errorCode":null,"errorMessage":"sse: Handler must not be nil","messagePattern":"sse: Handler must not be nil","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/sse/sse.go","lineNumber":27,"sourceCode":"import (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/gofiber/fiber/v3\"\n\t\"github.com/gofiber/utils/v2\"\n)\n\nvar errStreamClosed = errors.New(\"sse: stream closed\")\n\n// New creates a new SSE handler.\nfunc New(config ...Config) fiber.Handler {\n\tcfg := configDefault(config...)\n\tif cfg.Handler == nil {\n\t\tpanic(\"sse: Handler must not be nil\")\n\t}\n\n\treturn func(c fiber.Ctx) error {\n\t\tc.Set(fiber.HeaderContentType, fiber.MIMETextEventStream)\n\t\tc.Set(fiber.HeaderCacheControl, \"no-cache\")\n\t\tc.Set(fiber.HeaderConnection, \"keep-alive\")\n\t\tc.Set(\"X-Accel-Buffering\", \"no\")\n\n\t\tstreamContext := c.Context()\n\t\tlastEventID := c.Get(fiber.HeaderLastEventID)\n\n\t\tc.Abandon()\n\n\t\treturn c.SendStreamWriter(func(w *bufio.Writer) {\n\t\t\tstream := newStream(streamContext, w, lastEventID, c.App().Config().JSONEncoder)\n\t\t\tvar streamErr error\n\t\t\tdefer func() {\n\t\t\t\tif cfg.OnClose != nil {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/sse/sse.go#L9-L45","documentation":"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.","triggerScenarios":"sse.New(sse.Config{}) with no Handler field, or sse.New() followed by code that expected a default — there is no default Handler.","commonSituations":"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.","solutions":["Provide a Handler when constructing the config: sse.New(sse.Config{Handler: func(c fiber.Ctx, s *sse.Stream) error { ... }}).","If you build the Config incrementally, assert cfg.Handler != nil before calling sse.New in tests."],"exampleFix":"// before\napp.Get(\"/events\", sse.New()) // panics: no Handler\n\n// after\napp.Get(\"/events\", sse.New(sse.Config{\n    Handler: func(c fiber.Ctx, s *sse.Stream) error {\n        return s.WriteEvent(\"ping\", \"hello\")\n    },\n}))","handlingStrategy":"validation","validationCode":"func newSSE(cfg sse.Config) fiber.Handler {\n    if cfg.Handler == nil {\n        log.Fatal(\"sse: Config.Handler must be set\")\n    }\n    return sse.New(cfg)\n}","typeGuard":"func hasSSEHandler(cfg sse.Config) bool { return cfg.Handler != nil }","tryCatchPattern":null,"preventionTips":["Wrap sse.New in a helper that asserts Handler is set so the failure is a clear log line, not a raw panic.","In tests, construct the Config through a builder that requires a Handler argument."],"tags":["sse","config","panic","middleware"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}