{"record":{"id":"9ddf10af479275bb","repo":"gofiber/fiber","slug":"sse-handler-panic-v","errorCode":null,"errorMessage":"sse: handler panic: %v","messagePattern":"sse: handler panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/sse/sse.go","lineNumber":55,"sourceCode":"\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 {\n\t\t\t\t\tfinalErr := streamErr\n\t\t\t\t\tif finalErr == nil {\n\t\t\t\t\t\tfinalErr = stream.Err()\n\t\t\t\t\t}\n\t\t\t\t\tcfg.OnClose(c, finalErr)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tdefer func() {\n\t\t\t\tif recovered := recover(); recovered != nil {\n\t\t\t\t\tstreamErr = fmt.Errorf(\"sse: handler panic: %v\", recovered)\n\t\t\t\t}\n\t\t\t}()\n\t\t\tdefer stream.closeStream()\n\n\t\t\tif cfg.Retry > 0 {\n\t\t\t\tstreamErr = stream.Retry(cfg.Retry)\n\t\t\t\tif streamErr != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !cfg.DisableHeartbeat {\n\t\t\t\tstopHeartbeat := stream.startHeartbeat(cfg.HeartbeatInterval)\n\t\t\t\tif stopHeartbeat != nil {\n\t\t\t\t\tdefer stopHeartbeat()\n\t\t\t\t}\n\t\t\t}\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/sse/sse.go#L37-L73","documentation":"Set as streamErr when the user-provided SSE handler function (cfg.Handler) panics. A deferred recover catches the panic value and converts it into this error, which is then delivered to the OnClose callback as the stream's final error. The %v is the recovered panic value (not wrapped, since panic values are arbitrary types).","triggerScenarios":"Any panic inside the handler closure passed to sse.NewWithConfig's Handler — nil pointer dereference, index/slice out of range, type assertion failure, or an explicit panic() call.","commonSituations":"Bugs in the event-producing handler (nil map writes, unchecked type assertions, out-of-bounds slices); third-party code inside the handler that panics; nil pointer on a context field.","solutions":["Do not panic in SSE handlers — return errors and let the middleware handle stream teardown.","If you call code that may panic, wrap it in your own recover and convert to an error.","Inspect the recovered value/stack (logged via OnClose) to find and fix the root cause.","Add table tests for handler edge cases (nil inputs, empty slices)."],"exampleFix":"// before\nstreamErr = cfg.Handler(c, stream) // a panic here is caught, stream ends\n\n// after — make the handler panic-safe at the source\nfunc handler(c fiber.Ctx, s *sse.Stream) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"handler recovered: %v\", r)\n        }\n    }()\n    // ... produce events, returning errors instead of panicking\n}","handlingStrategy":"try-catch","validationCode":"// Wrap risky handler logic so a panic becomes an error you control.\nfunc safeHandler(c fiber.Ctx, s *sse.Stream) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"handler recovered: %v\", r)\n        }\n    }()\n    return produceEvents(c, s)\n}","typeGuard":null,"tryCatchPattern":"// The middleware already recovers; use OnClose to observe the panic-derived error.\ncfg := sse.NewWithConfig(sse.Config{\n    Handler: myHandler,\n    OnClose: func(c fiber.Ctx, err error) {\n        if err != nil { log.Printf(\"stream ended: %v\", err) }\n    },\n})","preventionTips":["Return errors from handlers instead of panicking.","Guard against nil maps/slices and unchecked type assertions inside the handler.","Add unit tests covering nil/empty inputs.","Log OnClose errors to catch silent panics in production."],"tags":["sse","panic","recovery","handler"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}