From ee6b852959ce70807705f3891391b33a245ab494 Mon Sep 17 00:00:00 2001 From: Spiegel Date: Sat, 9 May 2026 11:20:37 +0900 Subject: [PATCH] fix: guard nil Err and propagate zap context encode errors --- errs.go | 8 +++++++- errs_test.go | 20 ++++++++++++++++++++ zapobject/zapobject.go | 4 +++- zapobject/zapobject_test.go | 26 ++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 zapobject/zapobject_test.go diff --git a/errs.go b/errs.go index 7d15b1d..0600e2f 100644 --- a/errs.go +++ b/errs.go @@ -141,12 +141,18 @@ func (e *Error) Error() string { if e == nil { return nilAngleString } - errMsg := e.Err.Error() + var errMsg string + if e.Err != nil { + errMsg = e.Err.Error() + } var causeMsg string if e.Cause != nil { causeMsg = e.Cause.Error() } if len(causeMsg) == 0 { + if len(errMsg) == 0 { + return nilAngleString + } return errMsg } if len(errMsg) == 0 { diff --git a/errs_test.go b/errs_test.go index 1daa8b6..2ea39c7 100644 --- a/errs_test.go +++ b/errs_test.go @@ -447,6 +447,26 @@ func TestUnwraps(t *testing.T) { } } +func TestErrorWithNilErr(t *testing.T) { + testCases := []struct { + name string + err *Error + want string + }{ + {name: "empty", err: &Error{}, want: ""}, + {name: "cause only", err: &Error{Cause: os.ErrInvalid}, want: "invalid argument"}, + {name: "err and cause", err: &Error{Err: os.ErrNotExist, Cause: os.ErrInvalid}, want: "file does not exist: invalid argument"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if got := tc.err.Error(); got != tc.want { + t.Errorf("Error() = %q, want %q", got, tc.want) + } + }) + } +} + /* Copyright 2019-2023 Spiegel * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/zapobject/zapobject.go b/zapobject/zapobject.go index 184cb4d..1427063 100644 --- a/zapobject/zapobject.go +++ b/zapobject/zapobject.go @@ -45,7 +45,9 @@ func (e ErrObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { sort.Strings(keys) enc.OpenNamespace("context") for _, k := range keys { - _ = enc.AddReflected(k, ee.Context[k]) + if err := enc.AddReflected(k, ee.Context[k]); err != nil { + return err + } } } } else { diff --git a/zapobject/zapobject_test.go b/zapobject/zapobject_test.go new file mode 100644 index 0000000..9913c38 --- /dev/null +++ b/zapobject/zapobject_test.go @@ -0,0 +1,26 @@ +package zapobject + +import ( + "errors" + "testing" + + "github.com/goark/errs" + "go.uber.org/zap/zapcore" +) + +type addReflectedErrEncoder struct { + *zapcore.MapObjectEncoder +} + +func (e *addReflectedErrEncoder) AddReflected(_ string, _ interface{}) error { + return errors.New("add reflected failed") +} + +func TestMarshalLogObject_AddReflectedError(t *testing.T) { + err := errs.New("wrapped", errs.WithContext("bad", func() {})) + enc := &addReflectedErrEncoder{MapObjectEncoder: zapcore.NewMapObjectEncoder()} + + if got := New(err).MarshalLogObject(enc); got == nil { + t.Fatal("MarshalLogObject() = nil, want non-nil error") + } +}