[PR #13822] fix: retry failed LSP servers with exponential backoff (#13785) #14825

Open
opened 2026-02-16 18:19:34 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/13822

State: open
Merged: No


Problem

When an LSP server fails to spawn or initialize, it is added to a broken: Set<string> and permanently disabled for the entire session. If the failure was transient (e.g., temporary resource exhaustion, race condition), the user loses all LSP functionality for that server until they restart opencode.

Solution

Replace broken: Set<string> with broken: Map<string, { count: number; until: number }> to implement exponential backoff retry:

Attempt Backoff
1st failure 30s
2nd failure 60s
3rd failure 120s
4th failure 240s
5th failure 480s
>5 failures permanently disabled

Key changes in packages/opencode/src/lsp/index.ts:

  1. Added LSP_RETRY_MAX (5) and LSP_RETRY_BASE_MS (30s) constants
  2. Added BrokenEntry interface: { count: number; until: number }
  3. Added markBroken() helper — increments failure count and computes next retry time using base * 2^(count-1)
  4. Added isBroken() helper — returns true if permanently broken (count > max) or still within backoff window; returns false once the backoff period expires, allowing a retry
  5. Replaced all s.broken.add(key)markBroken(s.broken, key)
  6. Replaced all s.broken.has(key)isBroken(s.broken, key)

Behavior

  • Transient failures now self-heal after the backoff period expires
  • Persistent failures (>5 attempts) are still permanently disabled, preserving the existing safety behavior
  • No changes to the public API or other modules

Closes #13785

**Original Pull Request:** https://github.com/anomalyco/opencode/pull/13822 **State:** open **Merged:** No --- ## Problem When an LSP server fails to spawn or initialize, it is added to a `broken: Set<string>` and **permanently disabled** for the entire session. If the failure was transient (e.g., temporary resource exhaustion, race condition), the user loses all LSP functionality for that server until they restart opencode. ## Solution Replace `broken: Set<string>` with `broken: Map<string, { count: number; until: number }>` to implement exponential backoff retry: | Attempt | Backoff | |---------|---------| | 1st failure | 30s | | 2nd failure | 60s | | 3rd failure | 120s | | 4th failure | 240s | | 5th failure | 480s | | >5 failures | permanently disabled | ### Key changes in `packages/opencode/src/lsp/index.ts`: 1. Added `LSP_RETRY_MAX` (5) and `LSP_RETRY_BASE_MS` (30s) constants 2. Added `BrokenEntry` interface: `{ count: number; until: number }` 3. Added `markBroken()` helper — increments failure count and computes next retry time using `base * 2^(count-1)` 4. Added `isBroken()` helper — returns `true` if permanently broken (count > max) or still within backoff window; returns `false` once the backoff period expires, allowing a retry 5. Replaced all `s.broken.add(key)` → `markBroken(s.broken, key)` 6. Replaced all `s.broken.has(key)` → `isBroken(s.broken, key)` ### Behavior - Transient failures now self-heal after the backoff period expires - Persistent failures (>5 attempts) are still permanently disabled, preserving the existing safety behavior - No changes to the public API or other modules Closes #13785
yindo added the pull-request label 2026-02-16 18:19:34 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#14825