{"id":"9ddf10af479275bb","repo":"gofiber/fiber","slug":"sse-handler-panic-v","errorCode":null,"errorMessage":"sse: handler panic: %v","messagePattern":"sse: handler panic: (.+?)","errorType":"exception","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/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/sse/sse.go#L37-L73","documentation":"Fiber wraps your SSE handler in a recover; if the handler panics, the recovered value is wrapped as 'sse: handler panic: %v', the stream is closed, and OnClose (if set) receives this error. This keeps one client's panic from crashing the whole server process.","triggerScenarios":"Any panic inside the function passed to sse.New(Config{Handler: ...}) while serving a client: nil-pointer dereference, index-out-of-range, concurrent map write, failed type assertion without ok.","commonSituations":"Unhandled nil from a DB driver or map lookup; concurrent map writes; type assertions on interface values from the stream/ctx; third-party lib panics.","solutions":["Inspect the wrapped panic message to locate the panic site and fix the root cause.","Add comma-ok type assertions and nil checks for values from ctx/external calls.","Protect shared maps/state with a mutex; avoid concurrent map writes."],"exampleFix":"// before\nHandler: func(c fiber.Ctx, s *sse.Stream) error {\n    name := c.Locals(\"user\").(*User).Name // (*User)(nil).Name -> panic\n    ...\n}\n\n// after\nHandler: func(c fiber.Ctx, s *sse.Stream) error {\n    u, ok := c.Locals(\"user\").(*User)\n    if !ok || u == nil {\n        return c.SendStatus(fiber.StatusUnauthorized)\n    }\n    name := u.Name\n    ...\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Wrap risky handler logic in your own recover for structured logging.\nfunc safe(fn fiber.Handler) fiber.Handler {\n    return func(c fiber.Ctx) (err error) {\n        defer func() {\n            if r := recover(); r != nil {\n                log.Errorf(\"handler panic: %v\\n%s\", r, debug.Stack())\n                err = fiber.ErrInternalServerError\n            }\n        }()\n        return fn(c)\n    }\n}","preventionTips":["Use comma-ok for all type assertions.","Guard nil pointers from DB/map lookups.","Never write to a map concurrently; use sync.Map or a mutex."],"tags":["sse","panic","recovery","fiber"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}