@@ -361,6 +361,22 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
361361 */
362362 assistantMessageSavedToHistory = false
363363
364+ /**
365+ * Fire-and-forget wrapper around `presentAssistantMessage` that swallows the
366+ * expected cancellation rejection (the presenter throws when `this.abort` is set)
367+ * and logs any other failure. Keeping it non-blocking preserves the streaming
368+ * presenter's self-locking semantics while preventing unhandled promise rejections
369+ * from crashing the extension host.
370+ */
371+ private presentAssistantMessageSafe ( ) : void {
372+ void presentAssistantMessage ( this ) . catch ( ( error ) => {
373+ if ( this . abort ) {
374+ return
375+ }
376+ console . error ( `[Task#presentAssistantMessage] task ${ this . taskId } .${ this . instanceId } failed:` , error )
377+ } )
378+ }
379+
364380 /**
365381 * Push a tool_result block to userMessageContent, preventing duplicates.
366382 * Duplicate tool_use_ids cause API errors.
@@ -524,7 +540,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
524540 this . messageQueueStateChangedHandler = ( ) => {
525541 this . emit ( RooCodeEventName . TaskUserMessage , this . taskId )
526542 this . emit ( RooCodeEventName . QueuedMessagesUpdated , this . taskId , this . messageQueueService . messages )
527- void this . providerRef . deref ( ) ?. postStateToWebviewWithoutTaskHistory ( )
543+ void this . providerRef
544+ . deref ( )
545+ ?. postStateToWebviewWithoutTaskHistory ( )
546+ . catch ( ( error ) => {
547+ console . error ( "[Task#messageQueueStateChangedHandler] postStateToWebviewWithoutTaskHistory failed:" , error )
548+ } )
528549 }
529550
530551 this . messageQueueService . on ( "stateChanged" , this . messageQueueStateChangedHandler )
@@ -569,9 +590,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
569590 if ( startTask ) {
570591 this . _started = true
571592 if ( task || images ) {
572- void this . startTask ( task , images )
593+ void this . startTask ( task , images ) . catch ( ( error ) => {
594+ console . error ( "[Task#constructor] startTask failed:" , error )
595+ } )
573596 } else if ( historyItem ) {
574- void this . resumeTaskFromHistory ( )
597+ void this . resumeTaskFromHistory ( ) . catch ( ( error ) => {
598+ console . error ( "[Task#constructor] resumeTaskFromHistory failed:" , error )
599+ } )
575600 } else {
576601 throw new Error ( "Either historyItem or task/images must be provided" )
577602 }
@@ -1153,6 +1178,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11531178 // data or one whole message at a time so ignore partial for
11541179 // saves, and only post parts of partial message instead of
11551180 // whole array in new listener.
1181+ /* v8 ignore next -- fire-and-forget webview update; rejection is benign */
11561182 void this . updateClineMessage ( lastMessage )
11571183 // console.log("Task#ask: current ask promise was ignored (#1)")
11581184 throw new AskIgnoredError ( "updating existing partial" )
@@ -1191,6 +1217,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
11911217 lastMessage . progressStatus = progressStatus
11921218 lastMessage . isProtected = isProtected
11931219 await this . saveClineMessages ( )
1220+ /* v8 ignore next -- fire-and-forget webview update; rejection is benign */
11941221 void this . updateClineMessage ( lastMessage )
11951222 } else {
11961223 // This is a new and complete message, so add it like normal.
@@ -1253,6 +1280,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
12531280 if ( message ) {
12541281 this . interactiveAsk = message
12551282 this . emit ( RooCodeEventName . TaskInteractive , this . taskId )
1283+ /* v8 ignore next -- fire-and-forget webview notification inside setTimeout; rejection is benign */
12561284 void provider ?. postMessageToWebview ( { type : "interactionRequired" } )
12571285 }
12581286 } , statusMutationTimeout ) ,
@@ -1784,7 +1812,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
17841812 const { task, images } = this . metadata
17851813
17861814 if ( task || images ) {
1787- void this . startTask ( task ?? undefined , images ?? undefined )
1815+ void this . startTask ( task ?? undefined , images ?? undefined ) . catch ( ( error ) => {
1816+ console . error ( "[Task#start] startTask failed:" , error )
1817+ } )
17881818 }
17891819 }
17901820
@@ -2768,7 +2798,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
27682798 // Add to content and present
27692799 this . assistantMessageContent . push ( partialToolUse )
27702800 this . userMessageContentReady = false
2771- void presentAssistantMessage ( this )
2801+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2802+ this . presentAssistantMessageSafe ( )
27722803 } else if ( event . type === "tool_call_delta" ) {
27732804 // Process chunk using streaming JSON parser
27742805 const partialToolUse = NativeToolCallParser . processStreamingChunk (
@@ -2787,7 +2818,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
27872818 this . assistantMessageContent [ toolUseIndex ] = partialToolUse
27882819
27892820 // Present updated tool use
2790- void presentAssistantMessage ( this )
2821+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2822+ this . presentAssistantMessageSafe ( )
27912823 }
27922824 }
27932825 } else if ( event . type === "tool_call_end" ) {
@@ -2813,7 +2845,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28132845 this . userMessageContentReady = false
28142846
28152847 // Present the finalized tool call
2816- void presentAssistantMessage ( this )
2848+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2849+ this . presentAssistantMessageSafe ( )
28172850 } else if ( toolUseIndex !== undefined ) {
28182851 // finalizeStreamingToolCall returned null (malformed JSON or missing args)
28192852 // Mark the tool as non-partial so it's presented as complete, but execution
@@ -2832,7 +2865,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28322865 this . userMessageContentReady = false
28332866
28342867 // Present the tool call - validation will handle missing params
2835- void presentAssistantMessage ( this )
2868+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2869+ this . presentAssistantMessageSafe ( )
28362870 }
28372871 }
28382872 }
@@ -2865,7 +2899,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28652899
28662900 // Present the tool call to user - presentAssistantMessage will execute
28672901 // tools sequentially and accumulate all results in userMessageContent
2868- void presentAssistantMessage ( this )
2902+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2903+ this . presentAssistantMessageSafe ( )
28692904 break
28702905 }
28712906 case "text" : {
@@ -2884,7 +2919,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
28842919 } )
28852920 this . userMessageContentReady = false
28862921 }
2887- void presentAssistantMessage ( this )
2922+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
2923+ this . presentAssistantMessageSafe ( )
28882924 break
28892925 }
28902926 }
@@ -3232,7 +3268,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32323268 this . userMessageContentReady = false
32333269
32343270 // Present the finalized tool call
3235- void presentAssistantMessage ( this )
3271+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
3272+ this . presentAssistantMessageSafe ( )
32363273 } else if ( toolUseIndex !== undefined ) {
32373274 // finalizeStreamingToolCall returned null (malformed JSON or missing args)
32383275 // We still need to mark the tool as non-partial so it gets executed
@@ -3251,7 +3288,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32513288 this . userMessageContentReady = false
32523289
32533290 // Present the tool call - validation will handle missing params
3254- void presentAssistantMessage ( this )
3291+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
3292+ this . presentAssistantMessageSafe ( )
32553293 }
32563294 }
32573295 }
@@ -3450,7 +3488,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
34503488 // If there is content to update then it will complete and
34513489 // update `this.userMessageContentReady` to true, which we
34523490 // `pWaitFor` before making the next request.
3453- void presentAssistantMessage ( this )
3491+ /* v8 ignore next -- streaming presenter; .catch lives in presentAssistantMessageSafe (covered) */
3492+ this . presentAssistantMessageSafe ( )
34543493 }
34553494
34563495 if ( hasTextContent || hasToolUses ) {
0 commit comments