{"record":{"id":"105df1e6ca09b1bb","repo":"gofiber/fiber","slug":"v","errorCode":null,"errorMessage":"%v","messagePattern":"%v","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/recover/recover.go","lineNumber":22,"sourceCode":"\t\"fmt\"\n\t\"os\"\n\t\"runtime/debug\"\n\n\t\"github.com/gofiber/fiber/v3\"\n)\n\n// Must not start with \"panic: \": the panic was recovered, and that exact prefix\n// makes gotestsum treat the whole run as crashed and skip --rerun-fails.\nfunc defaultStackTraceHandler(_ fiber.Ctx, e any) {\n\tfmt.Fprintf(os.Stderr, \"recovered panic: %v\\n\\n%s\\n\", e, debug.Stack())\n}\n\n// DefaultPanicHandler returns r directly if it's an error, and creates a new one with the %v verb otherwise.\nfunc DefaultPanicHandler(_ fiber.Ctx, r any) error {\n\tif err, ok := r.(error); ok {\n\t\treturn err\n\t}\n\treturn fmt.Errorf(\"%v\", r)\n}\n\n// New creates a new middleware handler\nfunc New(config ...Config) fiber.Handler {\n\t// Set default config\n\tcfg := configDefault(config...)\n\n\t// Return new handler\n\treturn func(c fiber.Ctx) (err error) { //nolint:nonamedreturns // Uses recover() to overwrite the error\n\t\t// Don't execute middleware if Next returns true\n\t\tif cfg.Next != nil && cfg.Next(c) {\n\t\t\treturn c.Next()\n\t\t}\n\n\t\t// Catch panics\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tif cfg.EnableStackTrace {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/recover/recover.go#L4-L40","documentation":"DefaultPanicHandler is invoked by the recover middleware when a panic occurs and the recovered value r is not an error. It formats r with %v into a new error via fmt.Errorf, losing the original type but producing a stable error message. This becomes the error returned to fiber's error handler. The middleware deliberately avoids the 'panic: ' prefix in its stack-trace handler so gotestsum --rerun-fails is not confused.","triggerScenarios":"Any panic with a non-error value: panic(\"boom\"), panic(42), panic(map[...]...), panic(nil) (recovered as nil, but recover() returns nil only when no panic — so in practice a non-error typed value). The classic case is panic(\"some string\") from a library that panics on strings.","commonSituations":"A third-party package panics with a string instead of an error; a goroutine in a handler panics with a custom struct; array/slice out-of-bounds (panics with a runtime.Error, which IS an error and would take the other branch); nil-pointer dereference (runtime.Error, takes the error branch).","solutions":["Find the panic origin from the stack trace logged by the recover middleware's StackTraceHandler (when EnableStackTrace is true).","Replace panic(stringVal) at the source with panic(errors.New(...)) or return an error so the original type is preserved.","Set a custom PanicHandler in recover.Config that type-switches on the recovered value to extract more detail.","Enable EnableStackTrace: true so the goroutine stack is in your logs.","Add unit tests that exercise the panicking code path and assert on the recovered error."],"exampleFix":"// before: library panics with a string, type info lost\npanic(\"invalid config\")\n\n// after: panic with an error so DefaultPanicHandler returns it unchanged\npanic(errors.New(\"invalid config\"))\n\n// or: install a richer PanicHandler\nrecover.New(recover.Config{\n  PanicHandler: func(_ fiber.Ctx, r any) error {\n    if e, ok := r.(error); ok { return e }\n    return fmt.Errorf(\"recovered: %v\", r)\n  },\n})","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// Type-switch recovered values for richer handling.\nfunc classifyPanic(r any) error {\n  switch v := r.(type) {\n  case error: return v\n  case string: return errors.New(v)\n  default: return fmt.Errorf(\"recovered non-error value: %#v\", v)\n  }\n}","tryCatchPattern":"// Install a custom PanicHandler that preserves type info.\napp.Use(recover.New(recover.Config{\n  EnableStackTrace: true,\n  PanicHandler: func(_ fiber.Ctx, r any) error {\n    if e, ok := r.(error); ok { return e }\n    return fmt.Errorf(\"recovered: %v\", r)\n  },\n}))","preventionTips":["Always panic with an error, not a string, so type info survives.","Enable EnableStackTrace to capture the goroutine stack.","Install a custom PanicHandler that type-switches recovered values.","Add tests for panicking code paths.","Alert on any recovered panic in production."],"tags":["recover","panic","middleware","error-handling"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}