fix: prevent infinite loop when canceling during auto-retry (#8902)

* fix: prevent infinite loop when canceling during auto-retry

- Add abort check after backoffAndAnnounce in first-chunk retry logic
- Add abort check after backoffAndAnnounce in mid-stream retry logic
- Properly handle task abortion to break retry loops

Fixes #8901

* docs: add critical comments explaining abort checks

- Document the importance of abort checks after backoff
- Explain how these checks prevent infinite loops
- Add context for future maintainability

---------

Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
roomote[bot] 2025-10-30 19:12:30 -04:00 • committed by GitHub
parent f583be3c30
commit b284eddf40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2240,6 +2240,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
error,
streamingFailedMessage,
)
// Check if task was aborted during the backoff
if (this.abort) {
console.log(
`[Task#${this.taskId}.${this.instanceId}] Task aborted during mid-stream retry backoff`,
)
// Abort the entire task
this.abortReason = "user_cancelled"
await this.abortTask()
break
}
}
// Push the same content back onto the stack to retry, incrementing the retry attempt counter
@ -2790,6 +2801,15 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// Apply shared exponential backoff and countdown UX
await this.backoffAndAnnounce(retryAttempt, error, errorMsg)
// CRITICAL: Check if task was aborted during the backoff countdown
// This prevents infinite loops when users cancel during auto-retry
// Without this check, the recursive call below would continue even after abort
if (this.abort) {
throw new Error(
`[Task#attemptApiRequest] task ${this.taskId}.${this.instanceId} aborted during retry`,
)
}
// Delegate generator output from the recursive call with
// incremented retry count.
yield* this.attemptApiRequest(retryAttempt + 1)