diff --git a/errors.go b/errors.go index b0d70a94..e657a89f 100644 --- a/errors.go +++ b/errors.go @@ -5,6 +5,7 @@ package gin import ( + "errors" "fmt" "reflect" "strings" @@ -165,11 +166,32 @@ func (a errorMsgs) String() string { return "" } var buffer strings.Builder - for i, msg := range a { - fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err) - if msg.Meta != nil { - fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) + count := 1 + for _, msg := range a { + for _, err := range unwrapJoinErr(msg.Err) { + fmt.Fprintf(&buffer, "Error #%02d: %s\n", count, err) + if msg.Meta != nil { + fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) + } + count++ } } return buffer.String() } + +func unwrapJoinErr(err error) []error { + if err == nil { + return nil + } + var result []error + if multi, ok := err.(interface{ Unwrap() []error }); ok { + for _, e := range multi.Unwrap() { + result = append(result, unwrapJoinErr(e)...) + } + } else if single := errors.Unwrap(err); single != nil { + result = append(result, unwrapJoinErr(single)...) + } else { + result = append(result, err) + } + return result +} diff --git a/errors_test.go b/errors_test.go index 85ed3dd5..ef75ceb7 100644 --- a/errors_test.go +++ b/errors_test.go @@ -7,6 +7,7 @@ package gin import ( "errors" "fmt" + "strings" "testing" "github.com/gin-gonic/gin/internal/json" @@ -138,3 +139,26 @@ func TestErrorUnwrap(t *testing.T) { var testErrNonPointer TestErr require.ErrorAs(t, wrappedErr, &testErrNonPointer) } + +func TestErrorJoinFormatting(t *testing.T) { + // Create a context with errorMsgs slice + c, _ := CreateTestContext(nil) + + // Create a joined error + err1 := errors.New("service error") + err2 := errors.New("store error") + joined := errors.Join(err1, err2) + + // Add to context errors + if err := c.Error(joined); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Call String(), which should now unwrap and format both + output := c.Errors.String() + + // Check that both individual errors appear separately + if !strings.Contains(output, "Error #01: service error") || !strings.Contains(output, "Error #02: store error") { + t.Errorf("expected unwrapped errors in output, got:\n%s", output) + } +}