From 26e0786ddca924b634bc1da8c308be10de12fc08 Mon Sep 17 00:00:00 2001 From: hannesrudolph Date: Thu, 31 Jul 2025 16:59:08 -0600 Subject: [PATCH] fix(webview): harden postMessageToWebview with try/catch; replace magic 10ms with INCREMENTAL_SEND_DELAY_MS; guard Task message send calls with non-fatal errors --- src/core/task/Task.ts | 16 ++++++++++++++-- src/core/webview/ClineProvider.ts | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 865e8018a4..3727a7094e 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -563,7 +563,13 @@ export class Task extends EventEmitter implements TaskLike { this.clineMessages.push(message) const provider = this.providerRef.deref() // Send only the new message instead of the entire state - await provider?.postMessageToWebview({ type: "messageCreated", clineMessage: message }) + if (provider) { + try { + await provider.postMessageToWebview({ type: "messageCreated", clineMessage: message }) + } catch (error) { + // provider.postMessageToWebview already logs; leave as non-fatal + } + } this.emit(RooCodeEventName.Message, { action: "created", message }) await this.saveClineMessages() @@ -585,7 +591,13 @@ export class Task extends EventEmitter implements TaskLike { private async updateClineMessage(message: ClineMessage) { const provider = this.providerRef.deref() - await provider?.postMessageToWebview({ type: "messageUpdated", clineMessage: message }) + if (provider) { + try { + await provider.postMessageToWebview({ type: "messageUpdated", clineMessage: message }) + } catch (error) { + // provider.postMessageToWebview already logs; leave as non-fatal + } + } this.emit(RooCodeEventName.Message, { action: "updated", message }) const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled() diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5c845702d3..fafc814bb5 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -9,6 +9,9 @@ import axios from "axios" import pWaitFor from "p-wait-for" import * as vscode from "vscode" +// Small delay used for incremental message delivery to the webview +const INCREMENTAL_SEND_DELAY_MS = 10 + import { type TaskProviderLike, type TaskProviderEvents, @@ -888,7 +891,15 @@ export class ClineProvider } public async postMessageToWebview(message: ExtensionMessage) { - await this.view?.webview.postMessage(message) + try { + await this.view?.webview.postMessage(message) + } catch (error) { + // Guard against unhandled promise rejections from webview messaging + const errMsg = + error instanceof Error ? error.message : typeof error === "string" ? error : JSON.stringify(error) + this.log(`[postMessageToWebview] failed to post message '${message.type}': ${errMsg}`) + // Non-fatal: continue without throwing to avoid crashing extension host + } } private async getHMRHtmlContent(webview: vscode.Webview): Promise { @@ -1564,7 +1575,7 @@ export class ClineProvider for (const message of currentCline.clineMessages) { await this.postMessageToWebview({ type: "messageCreated", clineMessage: message }) // Small delay to prevent overwhelming the webview - await delay(10) + await delay(INCREMENTAL_SEND_DELAY_MS) } } else { // Normal state update for new tasks or tasks without messages