gofiber/fiber · error
%v
Error message
%v
What it means
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.
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.
Example fix
// before: library panics with a string, type info lost
panic("invalid config")
// after: panic with an error so DefaultPanicHandler returns it unchanged
panic(errors.New("invalid config"))
// or: install a richer PanicHandler
recover.New(recover.Config{
PanicHandler: func(_ fiber.Ctx, r any) error {
if e, ok := r.(error); ok { return e }
return fmt.Errorf("recovered: %v", r)
},
}) Defensive patterns
Strategy: try-catch
Type guard
// Type-switch recovered values for richer handling.
func classifyPanic(r any) error {
switch v := r.(type) {
case error: return v
case string: return errors.New(v)
default: return fmt.Errorf("recovered non-error value: %#v", v)
}
} Try / catch
// Install a custom PanicHandler that preserves type info.
app.Use(recover.New(recover.Config{
EnableStackTrace: true,
PanicHandler: func(_ fiber.Ctx, r any) error {
if e, ok := r.(error); ok { return e }
return fmt.Errorf("recovered: %v", r)
},
})) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- [session] AbsoluteTimeout must be greater than or equal to…
- sse: Handler must not be nil
- add: invalid http method
- basicauth: charset must be UTF-8
- client cannot be nil
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/105df1e6ca09b1bb.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/recover/recover.go:22
"fmt"
"os"
"runtime/debug"
"github.com/gofiber/fiber/v3"
)
// Must not start with "panic: ": the panic was recovered, and that exact prefix
// makes gotestsum treat the whole run as crashed and skip --rerun-fails.
func defaultStackTraceHandler(_ fiber.Ctx, e any) {
fmt.Fprintf(os.Stderr, "recovered panic: %v\n\n%s\n", e, debug.Stack())
}
// DefaultPanicHandler returns r directly if it's an error, and creates a new one with the %v verb otherwise.
func DefaultPanicHandler(_ fiber.Ctx, r any) error {
if err, ok := r.(error); ok {
return err
}
return fmt.Errorf("%v", r)
}
// New creates a new middleware handler
func New(config ...Config) fiber.Handler {
// Set default config
cfg := configDefault(config...)
// Return new handler
return func(c fiber.Ctx) (err error) { //nolint:nonamedreturns // Uses recover() to overwrite the error
// Don't execute middleware if Next returns true
if cfg.Next != nil && cfg.Next(c) {
return c.Next()
}
// Catch panics
defer func() {
if r := recover(); r != nil {
if cfg.EnableStackTrace {View on GitHub (pinned to a105acad6c)