11import 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' ;
310import { logAndExitProcess } from '../utils/errorhandling' ;
411
512type UnhandledRejectionMode = 'none' | 'warn' | 'strict' ;
613
14+ type IgnoreMatcher = { name ?: string | RegExp ; message ?: string | RegExp } ;
15+
716interface OnUnhandledRejectionOptions {
817 /**
918 * Option deciding what to do after capturing unhandledRejection,
1019 * that mimicks behavior of node's --unhandled-rejection flag.
1120 */
1221 mode : UnhandledRejectionMode ;
22+ /** Rejection Errors to ignore (don't capture or warn). */
23+ ignore ?: IgnoreMatcher [ ] ;
1324}
1425
1526const INTEGRATION_NAME = 'OnUnhandledRejection' ;
1627
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+
1734const _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+ } ;
2239
2340 return {
2441 name : INTEGRATION_NAME ,
@@ -28,27 +45,54 @@ const _onUnhandledRejectionIntegration = ((options: Partial<OnUnhandledRejection
2845 } ;
2946} ) satisfies IntegrationFn ;
3047
31- /**
32- * Add a global promise rejection handler.
33- */
3448export const onUnhandledRejectionIntegration = defineIntegration ( _onUnhandledRejectionIntegration ) ;
3549
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 */
4381export function makeUnhandledPromiseHandler (
4482 client : Client ,
4583 options : OnUnhandledRejectionOptions ,
4684) : ( reason : unknown , promise : unknown ) => void {
4785 return function sendUnhandledPromise ( reason : unknown , promise : unknown ) : void {
86+ // Only handle for the active client
4887 if ( getClient ( ) !== client ) {
4988 return ;
5089 }
5190
91+ // Skip if configured to ignore
92+ if ( matchesIgnore ( options . ignore ?? [ ] , reason ) ) {
93+ return ;
94+ }
95+
5296 const level : SeverityLevel = options . mode === 'strict' ? 'fatal' : 'error' ;
5397
5498 // this can be set in places where we cannot reliably get access to the active span/error
0 commit comments