Skip to content

Commit caf6142

Browse files
roomote[bot]roomotecte
authored
feat: add full error details to streaming failure dialog (#10131)
Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: cte <cestreich@gmail.com>
1 parent a7b192a commit caf6142

File tree

2 files changed

+13
-30
lines changed

2 files changed

+13
-30
lines changed

src/core/task/Task.ts

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,7 +2169,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21692169
break
21702170
} else {
21712171
const modelInfo = this.api.getModel().info
2172-
const state = await this.providerRef.deref()?.getState()
21732172
const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
21742173
nextUserContent = [{ type: "text", text: formatResponse.noToolsUsed(toolProtocol) }]
21752174
this.consecutiveMistakeCount++
@@ -2960,11 +2959,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
29602959
// Apply exponential backoff similar to first-chunk errors when auto-resubmit is enabled
29612960
const stateForBackoff = await this.providerRef.deref()?.getState()
29622961
if (stateForBackoff?.autoApprovalEnabled) {
2963-
await this.backoffAndAnnounce(
2964-
currentItem.retryAttempt ?? 0,
2965-
error,
2966-
streamingFailedMessage,
2967-
)
2962+
await this.backoffAndAnnounce(currentItem.retryAttempt ?? 0, error)
29682963

29692964
// Check if task was aborted during the backoff
29702965
if (this.abort) {
@@ -3066,10 +3061,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
30663061
// Need to save assistant responses to file before proceeding to
30673062
// tool use since user can exit at any moment and we wouldn't be
30683063
// able to save the assistant's response.
3069-
let didEndLoop = false
30703064

30713065
// Check if we have any content to process (text or tool uses)
30723066
const hasTextContent = assistantMessage.length > 0
3067+
30733068
const hasToolUses = this.assistantMessageContent.some(
30743069
(block) => block.type === "tool_use" || block.type === "mcp_tool_use",
30753070
)
@@ -3139,10 +3134,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
31393134
}
31403135

31413136
await this.addToApiConversationHistory(
3142-
{
3143-
role: "assistant",
3144-
content: assistantContent,
3145-
},
3137+
{ role: "assistant", content: assistantContent },
31463138
reasoningMessage || undefined,
31473139
)
31483140

@@ -3191,7 +3183,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
31913183
// Add periodic yielding to prevent blocking
31923184
await new Promise((resolve) => setImmediate(resolve))
31933185
}
3194-
// Continue to next iteration instead of setting didEndLoop from recursive call
3186+
31953187
continue
31963188
} else {
31973189
// If there's no assistant_responses, that means we got no text
@@ -3218,13 +3210,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
32183210
// Reuse the state variable from above
32193211
if (state?.autoApprovalEnabled) {
32203212
// Auto-retry with backoff - don't persist failure message when retrying
3221-
const errorMsg =
3222-
"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output."
3223-
32243213
await this.backoffAndAnnounce(
32253214
currentItem.retryAttempt ?? 0,
3226-
new Error("Empty assistant response"),
3227-
errorMsg,
3215+
new Error(
3216+
"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.",
3217+
),
32283218
)
32293219

32303220
// Check if task was aborted during the backoff
@@ -3811,18 +3801,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
38113801

38123802
// note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely.
38133803
if (autoApprovalEnabled) {
3814-
let errorMsg
3815-
3816-
if (error.error?.metadata?.raw) {
3817-
errorMsg = JSON.stringify(error.error.metadata.raw, null, 2)
3818-
} else if (error.message) {
3819-
errorMsg = error.message
3820-
} else {
3821-
errorMsg = "Unknown error"
3822-
}
3823-
38243804
// Apply shared exponential backoff and countdown UX
3825-
await this.backoffAndAnnounce(retryAttempt, error, errorMsg)
3805+
await this.backoffAndAnnounce(retryAttempt, error)
38263806

38273807
// CRITICAL: Check if task was aborted during the backoff countdown
38283808
// This prevents infinite loops when users cancel during auto-retry
@@ -3870,7 +3850,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
38703850
}
38713851

38723852
// Shared exponential backoff for retries (first-chunk and mid-stream)
3873-
private async backoffAndAnnounce(retryAttempt: number, error: any, header?: string): Promise<void> {
3853+
private async backoffAndAnnounce(retryAttempt: number, error: any): Promise<void> {
38743854
try {
38753855
const state = await this.providerRef.deref()?.getState()
38763856
const baseDelay = state?.requestDelaySeconds || 5
@@ -3900,7 +3880,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
39003880
}
39013881

39023882
const finalDelay = Math.max(exponentialDelay, rateLimitDelay)
3903-
if (finalDelay <= 0) return
3883+
if (finalDelay <= 0) {
3884+
return
3885+
}
39043886

39053887
// Build header text; fall back to error message if none provided
39063888
let headerText

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,7 @@ export const ChatRowContent = ({
10811081
? "https://github.com/cline/cline/wiki/TroubleShooting-%E2%80%90-%22PowerShell-is-not-recognized-as-an-internal-or-external-command%22"
10821082
: undefined
10831083
}
1084+
errorDetails={apiReqStreamingFailedMessage}
10841085
/>
10851086
)}
10861087
</>

0 commit comments

Comments
 (0)