Просмотр исходного кода

fix: suppress 'ask promise was ignored' error in handleError (#9914)

Daniel 3 недель назад
Родитель
Сommit
ee48b3a1ae

+ 11 - 0
src/core/assistant-message/presentAssistantMessage.ts

@@ -9,6 +9,7 @@ import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
 import type { ToolParamName, ToolResponse, ToolUse, McpToolUse } from "../../shared/tools"
 import { Package } from "../../shared/package"
 import { t } from "../../i18n"
+import { AskIgnoredError } from "../task/AskIgnoredError"
 
 import { fetchInstructionsTool } from "../tools/FetchInstructionsTool"
 import { listFilesTool } from "../tools/ListFilesTool"
@@ -225,6 +226,11 @@ export async function presentAssistantMessage(cline: Task) {
 			}
 
 			const handleError = async (action: string, error: Error) => {
+				// Silently ignore AskIgnoredError - this is an internal control flow
+				// signal, not an actual error. It occurs when a newer ask supersedes an older one.
+				if (error instanceof AskIgnoredError) {
+					return
+				}
 				const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
 				await cline.say(
 					"error",
@@ -613,6 +619,11 @@ export async function presentAssistantMessage(cline: Task) {
 			}
 
 			const handleError = async (action: string, error: Error) => {
+				// Silently ignore AskIgnoredError - this is an internal control flow
+				// signal, not an actual error. It occurs when a newer ask supersedes an older one.
+				if (error instanceof AskIgnoredError) {
+					return
+				}
 				const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
 
 				await cline.say(

+ 15 - 0
src/core/task/AskIgnoredError.ts

@@ -0,0 +1,15 @@
+/**
+ * Error thrown when an ask promise is superseded by a newer one.
+ *
+ * This is used as an internal control flow signal - not an actual error.
+ * It occurs when multiple asks are sent in rapid succession and an older
+ * ask is invalidated by a newer one (e.g., during streaming updates).
+ */
+export class AskIgnoredError extends Error {
+	constructor(reason?: string) {
+		super(reason ? `Ask ignored: ${reason}` : "Ask ignored")
+		this.name = "AskIgnoredError"
+		// Maintains proper prototype chain for instanceof checks
+		Object.setPrototypeOf(this, AskIgnoredError.prototype)
+	}
+}

+ 5 - 3
src/core/task/Task.ts

@@ -4,6 +4,8 @@ import os from "os"
 import crypto from "crypto"
 import EventEmitter from "events"
 
+import { AskIgnoredError } from "./AskIgnoredError"
+
 import { Anthropic } from "@anthropic-ai/sdk"
 import OpenAI from "openai"
 import delay from "delay"
@@ -983,7 +985,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					// whole array in new listener.
 					this.updateClineMessage(lastMessage)
 					// console.log("Task#ask: current ask promise was ignored (#1)")
-					throw new Error("Current ask promise was ignored (#1)")
+					throw new AskIgnoredError("updating existing partial")
 				} else {
 					// This is a new partial message, so add it with partial
 					// state.
@@ -992,7 +994,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					console.log(`Task#ask: new partial ask -> ${type} @ ${askTs}`)
 					await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, partial, isProtected })
 					// console.log("Task#ask: current ask promise was ignored (#2)")
-					throw new Error("Current ask promise was ignored (#2)")
+					throw new AskIgnoredError("new partial")
 				}
 			} else {
 				if (isUpdatingPreviousPartial) {
@@ -1146,7 +1148,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 			// command_output. It's important that when we know an ask could
 			// fail, it is handled gracefully.
 			console.log("Task#ask: current ask promise was ignored")
-			throw new Error("Current ask promise was ignored")
+			throw new AskIgnoredError("superseded")
 		}
 
 		const result = { response: this.askResponse!, text: this.askResponseText, images: this.askResponseImages }