uber-go/zap · warning
PANIC=%v
Error message
PANIC=%v
What it means
zapcore/field.go's encodeStringer recovers from panics raised while calling String() on a value passed with zap.Stringer (or similar). If String() panics, the field is encoded with the message "PANIC=%v" containing the recovered panic value rather than letting the panic escape the logging call.
Source
Thrown at zapcore/field.go:227
for i := range fields {
fields[i].AddTo(enc)
}
}
func encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (retErr error) {
// Try to capture panics (from nil references or otherwise) when calling
// the String() method, similar to https://golang.org/src/fmt/print.go#L540
defer func() {
if err := recover(); err != nil {
// If it's a nil pointer, just say "<nil>". The likeliest causes are a
// Stringer that fails to guard against nil or a nil pointer for a
// value receiver, and in either case, "<nil>" is a nice result.
if v := reflect.ValueOf(stringer); v.Kind() == reflect.Pointer && v.IsNil() {
enc.AddString(key, "<nil>")
return
}
retErr = fmt.Errorf("PANIC=%v", err)
}
}()
enc.AddString(key, stringer.(fmt.Stringer).String())
return nil
}
View on GitHub (pinned to bbd4ecbd87)
Solutions
- Make the String() method panic-safe: nil-check the receiver and any pointers it dereferences.
- Inspect the encoded PANIC=%v value in logs to identify the panicking Stringer and its panic reason.
- Stop logging the offending type with zap.Stringer until its String() is fixed (or log safe fields explicitly).
Example fix
// before
func (u *User) String() string { return u.Name } // panics when u is nil
// after
func (u *User) String() string {
if u == nil { return "<nil>" }
return u.Name
} Defensive patterns
Strategy: type-guard
Validate before calling
func safeString(v fmt.Stringer) (s string) {
defer func() { if recover() != nil { s = "<panic in String()>" } }()
return v.String()
}
logger.Info("user", zap.String("user", safeString(u))) Type guard
func stringerSafe(v any) (fmt.Stringer, bool) {
s, ok := v.(fmt.Stringer)
if !ok { return nil, false }
safe := func() (s string) {
defer func() { if recover() != nil { s = "<nil>" } }()
return s0(s)
}
_ = safe // wrap in your own safe Stringer type
return safeStringer{s}, true
} Prevention
- Write String() implementations that handle nil receivers and nil nested pointers.
- Avoid logging types whose String() depends on uninitialized state.
- Grep production logs for "PANIC=" to detect failing Stringers early.
When it happens
Trigger: Logging a type implementing fmt.Stringer whose String() method panics — nil pointer receiver dereference, index out of range, or calling methods on zero-value fields — via zap.Stringer(key, v).
Common situations: Stringers that dereference nested pointers which are nil, stringers over structs not yet initialized, mutex-guarded types whose String() is called from a context where state is invalid.
Related errors
- PANIC=%v
- can't unmarshal a nil *Level
- invalid increase level, as level %q is allowed by increased
- ce.Message
AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31).
Data as JSON: /api/errors/3796e731463526b8.
Report an issue: GitHub.