When a provider returns an empty assistant response (no text content and no tool calls), the model-response retry loop in Task.ts has no upper bound on retries. It resends the same unchanged request forever at the maximum exponential-backoff delay, with the only escape routes being manual cancellation or the provider eventually returning valid content on its own.
We observed this live with Claude Sonnet during a large file-write task: the extension made 5 requests / 4 retries over ~7 minutes against a frozen ~160k-token payload, receiving empty end_turn responses each time and only recovering on the 5th attempt. Users also reported that pressing Stop did not reliably escape the loop, because the graceful cancel path rehydrated the same task with its still-oversized history and the loop resumed immediately.
Root cause: retryAttempt increments with no upper-bound check in the empty-response branch; consecutiveNoAssistantMessagesCount only controls when the error toast is shown, not whether retries continue; MAX_EXPONENTIAL_BACKOFF_SECONDS caps the delay but not the count; and MAX_CONTEXT_WINDOW_RETRIES only applies to a different (context-exceeded) path that isn't reached here.
Note: the trigger is a transient provider-side empty response, which is outside our control — but the extension's reaction (unbounded retry, unreliable Stop) is a genuine robustness defect worth fixing.
When a provider returns an empty assistant response (no text content and no tool calls), the model-response retry loop in Task.ts has no upper bound on retries. It resends the same unchanged request forever at the maximum exponential-backoff delay, with the only escape routes being manual cancellation or the provider eventually returning valid content on its own.
We observed this live with Claude Sonnet during a large file-write task: the extension made 5 requests / 4 retries over ~7 minutes against a frozen ~160k-token payload, receiving empty end_turn responses each time and only recovering on the 5th attempt. Users also reported that pressing Stop did not reliably escape the loop, because the graceful cancel path rehydrated the same task with its still-oversized history and the loop resumed immediately.
Root cause: retryAttempt increments with no upper-bound check in the empty-response branch; consecutiveNoAssistantMessagesCount only controls when the error toast is shown, not whether retries continue; MAX_EXPONENTIAL_BACKOFF_SECONDS caps the delay but not the count; and MAX_CONTEXT_WINDOW_RETRIES only applies to a different (context-exceeded) path that isn't reached here.
Note: the trigger is a transient provider-side empty response, which is outside our control — but the extension's reaction (unbounded retry, unreliable Stop) is a genuine robustness defect worth fixing.