1
1
import type { Client , IntegrationFn , SeverityLevel , Span } from '@sentry/core' ;
2
- import { captureException , consoleSandbox , defineIntegration , getClient , withActiveSpan } from '@sentry/core' ;
2
+ import {
3
+ captureException ,
4
+ consoleSandbox ,
5
+ defineIntegration ,
6
+ getClient ,
7
+ isMatchingPattern ,
8
+ withActiveSpan ,
9
+ } from '@sentry/core' ;
3
10
import { logAndExitProcess } from '../utils/errorhandling' ;
4
11
5
12
type UnhandledRejectionMode = 'none' | 'warn' | 'strict' ;
6
13
14
+ type IgnoreMatcher = { name ?: string | RegExp ; message ?: string | RegExp } ;
15
+
7
16
interface OnUnhandledRejectionOptions {
8
17
/**
9
18
* Option deciding what to do after capturing unhandledRejection,
10
19
* that mimicks behavior of node's --unhandled-rejection flag.
11
20
*/
12
21
mode : UnhandledRejectionMode ;
22
+ /** Rejection Errors to ignore (don't capture or warn). */
23
+ ignore ?: IgnoreMatcher [ ] ;
13
24
}
14
25
15
26
const INTEGRATION_NAME = 'OnUnhandledRejection' ;
16
27
28
+ const DEFAULT_IGNORES : IgnoreMatcher [ ] = [
29
+ {
30
+ name : 'AI_NoOutputGeneratedError' , // When stream aborts in Vercel AI SDK, Vercel flush() fails with an error
31
+ } ,
32
+ ] ;
33
+
17
34
const _onUnhandledRejectionIntegration = ( ( options : Partial < OnUnhandledRejectionOptions > = { } ) => {
18
- const opts = {
19
- mode : 'warn' ,
20
- ...options ,
21
- } satisfies OnUnhandledRejectionOptions ;
35
+ const opts : OnUnhandledRejectionOptions = {
36
+ mode : options . mode ?? 'warn' ,
37
+ ignore : [ ...DEFAULT_IGNORES , ... ( options . ignore ?? [ ] ) ] ,
38
+ } ;
22
39
23
40
return {
24
41
name : INTEGRATION_NAME ,
@@ -28,27 +45,54 @@ const _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejection
28
45
} ;
29
46
} ) satisfies IntegrationFn ;
30
47
31
- /**
32
- * Add a global promise rejection handler.
33
- */
34
48
export const onUnhandledRejectionIntegration = defineIntegration ( _onUnhandledRejectionIntegration ) ;
35
49
36
- /**
37
- * Send an exception with reason
38
- * @param reason string
39
- * @param promise promise
40
- *
41
- * Exported only for tests.
42
- */
50
+ /** Extract error info safely */
51
+ function extractErrorInfo ( reason : unknown ) : { name : string ; message : string } {
52
+ // Check if reason is an object (including Error instances, not just plain objects)
53
+ if ( typeof reason !== 'object' || reason === null ) {
54
+ return { name : '' , message : String ( reason ?? '' ) } ;
55
+ }
56
+
57
+ const errorLike = reason as Record < string , unknown > ;
58
+ const name = typeof errorLike . name === 'string' ? errorLike . name : '' ;
59
+ const message = typeof errorLike . message === 'string' ? errorLike . message : String ( reason ) ;
60
+
61
+ return { name, message } ;
62
+ }
63
+
64
+ /** Check if a matcher matches the reason */
65
+ function isMatchingReason ( matcher : IgnoreMatcher , errorInfo : ReturnType < typeof extractErrorInfo > ) : boolean {
66
+ // name/message matcher
67
+ const nameMatches = matcher . name === undefined || isMatchingPattern ( errorInfo . name , matcher . name , true ) ;
68
+
69
+ const messageMatches = matcher . message === undefined || isMatchingPattern ( errorInfo . message , matcher . message ) ;
70
+
71
+ return nameMatches && messageMatches ;
72
+ }
73
+
74
+ /** Match helper */
75
+ function matchesIgnore ( list : IgnoreMatcher [ ] , reason : unknown ) : boolean {
76
+ const errorInfo = extractErrorInfo ( reason ) ;
77
+ return list . some ( matcher => isMatchingReason ( matcher , errorInfo ) ) ;
78
+ }
79
+
80
+ /** Core handler */
43
81
export function makeUnhandledPromiseHandler (
44
82
client : Client ,
45
83
options : OnUnhandledRejectionOptions ,
46
84
) : ( reason : unknown , promise : unknown ) => void {
47
85
return function sendUnhandledPromise ( reason : unknown , promise : unknown ) : void {
86
+ // Only handle for the active client
48
87
if ( getClient ( ) !== client ) {
49
88
return ;
50
89
}
51
90
91
+ // Skip if configured to ignore
92
+ if ( matchesIgnore ( options . ignore ?? [ ] , reason ) ) {
93
+ return ;
94
+ }
95
+
52
96
const level : SeverityLevel = options . mode === 'strict' ? 'fatal' : 'error' ;
53
97
54
98
// this can be set in places where we cannot reliably get access to the active span/error
0 commit comments