Skip to content
This repository was archived by the owner on May 15, 2026. It is now read-only.

Commit cedd25a

Browse files
hannesrudolphellipsis-dev[bot]
authored andcommitted
feat: improve 'no tools used' error handling with grace retry (#10196)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
1 parent 6733bcb commit cedd25a

21 files changed

Lines changed: 123 additions & 18 deletions

File tree

src/api/providers/gemini.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
318318
}
319319
}
320320

321-
// If we had reasoning but no content, emit a placeholder text to prevent "Empty assistant response" errors.
322-
// This typically happens when the model hits max output tokens while reasoning.
323-
if (hasReasoning && !hasContent) {
324-
let message = t("common:errors.gemini.thinking_complete_no_output")
325-
if (finishReason === "MAX_TOKENS") {
326-
message = t("common:errors.gemini.thinking_complete_truncated")
327-
} else if (finishReason === "SAFETY") {
328-
message = t("common:errors.gemini.thinking_complete_safety")
329-
} else if (finishReason === "RECITATION") {
330-
message = t("common:errors.gemini.thinking_complete_recitation")
331-
}
332-
333-
yield { type: "text", text: message }
334-
}
335-
336321
if (finalResponse?.responseId) {
337322
// Capture responseId so Task.addToApiConversationHistory can store it
338323
// alongside the assistant message in api_history.json.

src/core/task/Task.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
310310
consecutiveMistakeCount: number = 0
311311
consecutiveMistakeLimit: number
312312
consecutiveMistakeCountForApplyDiff: Map<string, number> = new Map()
313+
consecutiveNoToolUseCount: number = 0
313314
toolUsage: ToolUsage = {}
314315

315316
// Checkpoints
@@ -1982,6 +1983,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
19821983

19831984
this.abort = true
19841985

1986+
// Reset consecutive error counters on abort (manual intervention)
1987+
this.consecutiveNoToolUseCount = 0
1988+
19851989
// Force final token usage update before abort event
19861990
this.emitFinalTokenUsageUpdate()
19871991

@@ -2236,7 +2240,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
22362240
} else {
22372241
// Use the task's locked protocol, NOT the current settings (fallback to xml if not set)
22382242
nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml") }]
2239-
this.consecutiveMistakeCount++
22402243
}
22412244
}
22422245
}
@@ -3250,12 +3253,24 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32503253
)
32513254

32523255
if (!didToolUse) {
3256+
// Increment consecutive no-tool-use counter
3257+
this.consecutiveNoToolUseCount++
3258+
3259+
// Only show error and count toward mistake limit after 2 consecutive failures
3260+
if (this.consecutiveNoToolUseCount >= 2) {
3261+
await this.say("error", "MODEL_NO_TOOLS_USED")
3262+
// Only count toward mistake limit after second consecutive failure
3263+
this.consecutiveMistakeCount++
3264+
}
3265+
32533266
// Use the task's locked protocol for consistent behavior
32543267
this.userMessageContent.push({
32553268
type: "text",
32563269
text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml"),
32573270
})
3258-
this.consecutiveMistakeCount++
3271+
} else {
3272+
// Reset counter when tools are used successfully
3273+
this.consecutiveNoToolUseCount = 0
32593274
}
32603275

32613276
// Push to stack if there's content OR if we're paused waiting for a subtask.

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,22 @@ export const ChatRowContent = ({
12591259
</div>
12601260
)
12611261
case "error":
1262-
return <ErrorRow type="error" message={t("chat:error")} errorDetails={message.text || undefined} />
1262+
// Check if this is a model response error based on marker strings from backend
1263+
const isNoToolsUsedError = message.text === "MODEL_NO_TOOLS_USED"
1264+
1265+
if (isNoToolsUsedError) {
1266+
return (
1267+
<ErrorRow
1268+
type="error"
1269+
title={t("chat:modelResponseIncomplete")}
1270+
message={t("chat:modelResponseErrors.noToolsUsed")}
1271+
errorDetails={t("chat:modelResponseErrors.noToolsUsedDetails")}
1272+
/>
1273+
)
1274+
}
1275+
1276+
// Fallback for generic errors
1277+
return <ErrorRow type="error" message={message.text || t("chat:error")} />
12631278
case "completion_result":
12641279
return (
12651280
<>

webview-ui/src/i18n/locales/ca/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/de/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@
285285
},
286286
"taskCompleted": "Task Completed",
287287
"error": "Error",
288+
"modelResponseIncomplete": "Model Response Incomplete",
289+
"modelResponseErrors": {
290+
"noToolsUsed": "The model failed to use any tools in its response. This typically happens when the model provides only text/reasoning without calling the required tools to complete the task.",
291+
"noToolsUsedDetails": "The model provided text/reasoning but did not call any of the required tools. This usually indicates the model misunderstood the task or is having difficulty determining which tool to use. The model has been automatically prompted to retry with proper tool usage."
292+
},
288293
"errorDetails": {
289294
"title": "Error Details",
290295
"copyToClipboard": "Copy to Clipboard",

webview-ui/src/i18n/locales/es/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/fr/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/hi/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/id/chat.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)