Skip to content

Commit 11f2790

Browse files
authored
fix: add fallback on non hashable errors (#1113)
1 parent 2cf1dc0 commit 11f2790

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

exception.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,40 @@ const (
1313
MechanismSourceCause string = "cause"
1414
)
1515

16+
type visited struct {
17+
comparable map[error]struct{}
18+
msgs map[string]struct{}
19+
}
20+
21+
func (v *visited) seenError(err error) bool {
22+
t := reflect.TypeOf(err)
23+
if t == nil {
24+
return false
25+
}
26+
27+
if t.Comparable() {
28+
if _, ok := v.comparable[err]; ok {
29+
return true
30+
}
31+
v.comparable[err] = struct{}{}
32+
return false
33+
}
34+
35+
key := t.String() + err.Error()
36+
if _, ok := v.msgs[key]; ok {
37+
return true
38+
}
39+
v.msgs[key] = struct{}{}
40+
return false
41+
}
42+
1643
func convertErrorToExceptions(err error, maxErrorDepth int) []Exception {
1744
var exceptions []Exception
18-
visited := make(map[error]bool)
19-
convertErrorDFS(err, &exceptions, nil, "", visited, maxErrorDepth, 0)
45+
vis := &visited{
46+
make(map[error]struct{}),
47+
make(map[string]struct{}),
48+
}
49+
convertErrorDFS(err, &exceptions, nil, "", vis, maxErrorDepth, 0)
2050

2151
// mechanism type is used for debugging purposes, but since we can't really distinguish the origin of who invoked
2252
// captureException, we set it to nil if the error is not chained.
@@ -37,15 +67,14 @@ func convertErrorToExceptions(err error, maxErrorDepth int) []Exception {
3767
return exceptions
3868
}
3969

40-
func convertErrorDFS(err error, exceptions *[]Exception, parentID *int, source string, visited map[error]bool, maxErrorDepth int, currentDepth int) {
70+
func convertErrorDFS(err error, exceptions *[]Exception, parentID *int, source string, visited *visited, maxErrorDepth int, currentDepth int) {
4171
if err == nil {
4272
return
4373
}
4474

45-
if visited[err] {
75+
if visited.seenError(err) {
4676
return
4777
}
48-
visited[err] = true
4978

5079
_, isExceptionGroup := err.(interface{ Unwrap() []error })
5180

exception_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,21 @@ func TestCircularReferenceProtection(t *testing.T) {
377377
})
378378
}
379379
}
380+
381+
// unhashableSliceError is a non-comparable error type.
382+
type unhashableSliceError []string
383+
384+
func (e unhashableSliceError) Error() string {
385+
return "unhashable slice error"
386+
}
387+
388+
func TestConvertErrorToExceptions_UnhashableError_NoPanic(t *testing.T) {
389+
defer func() {
390+
if r := recover(); r != nil {
391+
t.Fatalf("convertErrorToExceptions panicked for unhashable error: %v", r)
392+
}
393+
}()
394+
395+
err := unhashableSliceError{"a", "b"}
396+
_ = convertErrorToExceptions(err, -1)
397+
}

0 commit comments

Comments
 (0)