Skip to content

feat(vscode): exponential backoff retry with cancel button for rate limiting#8435

Merged
catrielmuller merged 1 commit into
mainfrom
catrielmuller/fix-8333
Apr 7, 2026
Merged

feat(vscode): exponential backoff retry with cancel button for rate limiting#8435
catrielmuller merged 1 commit into
mainfrom
catrielmuller/fix-8333

Conversation

@catrielmuller

@catrielmuller catrielmuller commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Context

Implement exponential backoff retry with a cancel button for rate limiting (HTTP 429) and server errors in the VSCode extension. When the extension encounters rate limiting, it now automatically retries with increasing delays, and users can manually cancel the retry loop at any time.

This also addresses issue #8203 by clearing error messages when the user changes to a different model.

Implementation

  • Retry utility (packages/kilo-vscode/src/util/retry.ts): New module with backoff calculation functions

    • retryable(): Checks if HTTP status should be retried (429, 5xx, etc.)
    • backoff(): Calculates delay (5s -> 10s -> 30s -> 60s -> 300s)
    • headerDelay(): Extracts Retry-After headers
  • KiloProvider (packages/kilo-vscode/src/KiloProvider.ts):

    • Added withRetry() wrapper for SDK calls
    • Sends sessionStatus messages with retry countdown
    • Auto-stops after 5 failed attempts
  • WorkingIndicator (packages/kilo-vscode/webview-ui/src/components/shared/WorkingIndicator.tsx):

    • Added Cancel button during retry status
    • Button styled with error color for visibility
  • Session context (packages/kilo-vscode/webview-ui/src/context/session.tsx):

    • selectModel() now clears error messages when model changes
  • i18n: Added translations for 18 languages

Screenshots

image image b540-d9914a046e41" /> image image

How to Test

  1. Trigger a rate limit (429) error by making many requests
  2. Observe the retry countdown in the WorkingIndicator (5s -> 10s -> 30s -> 60s -> 300s)
  3. Click "Cancel" to stop the retry loop
  4. Send a message with a free model that hits the usage limit
  5. Switch to a paid model
  6. Verify the error message is cleared from the conversation

const status = result.response?.status ?? 0

// Check for non-retryable status codes — fail immediately without retry
if (!retryable(status) && attempt > 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Non-retryable errors still retry once

The immediate-fail guard is gated on attempt > 1, so a 400/401/403 response on the first call falls through to the backoff path and gets retried anyway. That adds an unnecessary second request for errors the user must fix locally.

Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
Comment thread packages/kilo-vscode/src/KiloProvider.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/src/KiloProvider.ts 2188 Cancelling during the retry backoff only clears the timeout, so the pending promise never settles and the send remains stuck indefinitely.
Other Observations (not in diff)

Previously reported issues in packages/kilo-vscode/src/KiloProvider.ts (non-retryable errors retrying once, the off-by-one retry cap, and cancellation surfacing as sendMessageFailed) appear resolved in this revision.

Previously reported model-switch crash in packages/kilo-vscode/webview-ui/src/context/session.tsx also appears resolved in this revision.

Files Reviewed (2 files)
  • packages/kilo-vscode/src/KiloProvider.ts - 1 issue
  • packages/kilo-vscode/webview-ui/src/context/session.tsx - 0 issues

Reviewed by gpt-5.4-20260305 · 325,004 tokens

@catrielmuller catrielmuller force-pushed the catrielmuller/fix-8333 branch from 65fb86d to 7d2fbad Compare April 6, 2026 20:50
const timer = setTimeout(resolve, delay)
abortController.signal.addEventListener("abort", () => {
clearTimeout(timer)
reject(new Error("Retry cancelled"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Cancelling a retry is surfaced as a send failure

This Error("Retry cancelled") bubbles into handleSendMessage() / handleSendCommand(), which then emits sendMessageFailed. The result is that clicking Cancel shows a failure toast and restores the prompt back into the composer even though the request was already accepted by the server; cancellation should follow the abort path instead of the generic send-failed path.

applyModel(selectedAgentName(), { providerID, modelID })
const sid = currentSessionID()
if (sid) {
setStore("messages", sid, (msgs) => msgs.filter((m) => !m.error))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Switching models can throw before messages finish loading

selectSession() sets currentSessionID before loadMessages populates store.messages[sid], so if the user changes models during that loading window msgs is still undefined here and .filter(...) crashes the webview.

Suggested change
setStore("messages", sid, (msgs) => msgs.filter((m) => !m.error))
setStore("messages", sid, (msgs = []) => msgs.filter((m) => !m.error))

…te limiting

Implement retry with exponential backoff when the extension encounters rate
limiting (HTTP 429) or server errors. Retries on: 5s -> 10s -> 30s ->
60s -> 300s, with a maximum of 5 attempts.

- Add retry utility module (retry.ts) with backoff calculation
- Add withRetry() wrapper for SDK calls in KiloProvider
- Add Cancel button to WorkingIndicator during retry status
- Clear error messages when model changes (issue #8203)
- Add i18n translations for cancel button (18 languages)

Users can manually cancel via the cancel button or the retry loop
automatically stops after 5 failed attempts.

Issues: #8333, #8203
@catrielmuller catrielmuller force-pushed the catrielmuller/fix-8333 branch from 7d2fbad to 613606b Compare April 6, 2026 22:04
})

// Wait for delay or until aborted
await new Promise((resolve) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Cancelling during backoff leaves the retry stuck

If abort fires while this sleep is pending, the handler only clears the timeout; it never resolves or rejects the surrounding promise. withRetry() then never reaches the next loop iteration or the finally block, so the cancelled send hangs indefinitely and the abort-controller entry is never cleaned up for that session.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imho it sounds like it makes sense

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's similar but works at different levels.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and we need it at multiple levels?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new file handle the errors between the VSCode Backend HTTP Client and the CLI Backend Server . The opencode/src/session/retry.ts handle the errors from the AI SDK Providers.

@catrielmuller catrielmuller merged commit 2bca86c into main Apr 7, 2026
14 checks passed
@catrielmuller catrielmuller deleted the catrielmuller/fix-8333 branch April 7, 2026 20:10
jliounis pushed a commit to jliounis/kilocode that referenced this pull request May 18, 2026
feat(vscode): exponential backoff retry with cancel button for rate limiting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants