From 9cd2f3bdde24573069204ef96aecb2bd8ec65564 Mon Sep 17 00:00:00 2001 From: Solaris-star <820622658@qq.com> Date: Wed, 22 Jul 2026 00:57:31 +0800 Subject: [PATCH] fix: treat typed nils as nil in Wrap/WrapPrefix Wrap and WrapPrefix only checked e == nil, so a nil pointer of a concrete error type (typed nil) was still wrapped. Calling Error() on that pointer then panicked. Use reflection to detect nilable kinds and return nil for typed nils. Fixes #35 --- error.go | 21 +++++++++++++++++++-- error_test.go | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/error.go b/error.go index b8aad09..e98ce3b 100644 --- a/error.go +++ b/error.go @@ -93,8 +93,25 @@ func New(e interface{}) *Error { // explicitly wrap an *Error with a new stacktrace use Errorf. The skip // parameter indicates how far up the stack to start the stacktrace. 0 is from // the current call, 1 from its caller, etc. -func Wrap(e interface{}, skip int) *Error { + +// isNil reports whether e is a nil interface value or a typed nil +// (nil pointer/slice/map/func/chan/interface). Zero-value structs that +// implement error are not considered nil. +func isNil(e interface{}) bool { if e == nil { + return true + } + v := reflect.ValueOf(e) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + return v.IsNil() + default: + return false + } +} + +func Wrap(e interface{}, skip int) *Error { + if isNil(e) { return nil } @@ -127,7 +144,7 @@ func Wrap(e interface{}, skip int) *Error { // the stack to start the stacktrace. 0 is from the current call, 1 from its // caller, etc. func WrapPrefix(e interface{}, prefix string, skip int) *Error { - if e == nil { + if isNil(e) { return nil } diff --git a/error_test.go b/error_test.go index 5f740e5..12eacc1 100644 --- a/error_test.go +++ b/error_test.go @@ -345,3 +345,24 @@ type errorString string func (e errorString) Error() string { return string(e) } + +type typedNilError struct{ message string } + +func (e *typedNilError) Error() string { return e.message } + +func TestWrapTypedNil(t *testing.T) { + var ptr *typedNilError + if Wrap(ptr, 0) != nil { + t.Errorf("Wrap typed nil pointer should return nil") + } + if WrapPrefix(ptr, "prefix", 0) != nil { + t.Errorf("WrapPrefix typed nil pointer should return nil") + } + if Wrap(nil, 0) != nil || WrapPrefix(nil, "p", 0) != nil { + t.Errorf("Wrap/WrapPrefix nil should return nil") + } + nonNil := &typedNilError{message: "x"} + if Wrap(nonNil, 0) == nil { + t.Errorf("Wrap non-nil pointer should wrap") + } +}