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

This commit is contained in:
Daniel 2025-12-08 20:19:45 -05:00 committed by GitHub
parent 88a0bed27f
commit ee48b3a1ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 31 additions and 3 deletions

View file

@ -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(

View file

@ -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)
}
}

View file

@ -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 }