Kyle Carberry
2bdacae5f5
feat(chatd): add LLM stream retry with exponential backoff (#22418)
## Summary
Adds automatic retry with exponential backoff for transient LLM errors
during chat streaming and title generation. Inspired by
[coder/mux](https://github.com/coder/mux)'s retry mechanism.
## Key Behaviors
- **Infinite retries** with exponential backoff: 1s → 2s → 4s → ... →
60s cap
- **Deterministic delays** (no jitter)
- **Error classification**: retryable (429, 5xx, overloaded, rate limit,
network errors) vs non-retryable (auth, quota, context exceeded, model
not found, canceled)
- **Retry status published to SSE stream** so frontend can show
"Retrying in Xs..." UI
- **Title generation** retries silently (best-effort, nil onRetry
callback)
## New Package: `coderd/chatd/chatretry/`
| File | Purpose |
|------|---------|
| `classify.go` | `IsRetryable(err)` and `StatusCodeRetryable(code)` |
| `backoff.go` | `Delay(attempt)` — exponential doubling with 60s cap |
| `retry.go` | `Retry(ctx, fn, onRetry)` — infinite loop with
context-aware timer |
## Test Helpers: `coderd/chatd/chattest/errors.go`
Anthropic and OpenAI error response builders for use in chattest
providers:
- `AnthropicErrorResponse()`, `AnthropicOverloadedResponse()`,
`AnthropicRateLimitResponse()`
- `OpenAIErrorResponse()`, `OpenAIRateLimitResponse()`,
`OpenAIServerErrorResponse()`
## SDK Changes: `codersdk/chats.go`
- New `ChatStreamEventType: "retry"`
- New `ChatStreamRetry` struct with `Attempt`, `DelayMs`, `Error`,
`RetryingAt` fields
- TypeScript types auto-generated
## Changed Files
- `coderd/chatd/chatloop/chatloop.go` — wraps `agent.Stream()` in
`chatretry.Retry()`
- `coderd/chatd/chatd.go` — publishes retry events to SSE stream with
logging
- `coderd/chatd/title.go` — wraps `model.Generate()` in silent retry
- `coderd/chatd/chattest/anthropic.go` / `openai.go` — error injection
support
## Tests
42 tests covering classification (33), backoff (9), and retry scenarios
(8).
2026-02-27 18:34:33 -05:00
..
2026-02-27 16:50:56 +00:00
2025-05-13 11:24:51 -05:00
2025-06-20 13:16:55 -06:00
2025-12-15 17:41:47 +00:00
2026-02-24 20:28:50 +02:00
2026-02-27 15:12:56 -05:00
2026-01-08 15:24:11 +04:00
2026-02-17 15:43:02 +01:00
2025-10-23 20:22:51 +03:00
2026-02-12 09:59:53 +02:00
2025-10-09 14:53:08 +02:00
2025-10-09 14:53:08 +02:00
2026-02-27 16:50:56 +00:00
2026-02-24 15:27:03 +00:00
2025-10-15 16:13:59 +00:00
2024-07-29 19:58:48 -05:00
2026-02-27 16:50:56 +00:00
2026-02-27 18:34:33 -05:00
2025-06-24 13:06:02 +02:00
2026-01-08 15:24:11 +04:00
2026-02-24 13:26:55 +04:00
2025-09-15 12:30:17 +10:00
2025-07-30 13:42:39 -07:00
2024-11-15 12:05:21 -06:00
2025-08-29 10:41:32 +02:00
2025-04-15 10:55:30 +02:00
2025-06-20 13:16:55 -06:00
2026-02-20 09:01:00 -06:00
2026-02-27 16:50:56 +00:00
2025-12-15 17:41:47 +00:00
2024-01-31 14:49:55 +01:00
2025-02-18 13:06:19 +01:00
2025-07-07 11:01:17 -06:00
2025-01-31 12:14:24 -07:00
2025-03-31 09:40:24 -03:00
2025-08-19 10:41:33 +02:00
2025-12-14 03:00:03 +00:00
2026-02-20 12:56:00 +11:00
2025-06-20 13:16:55 -06:00
2026-01-09 15:40:26 -07:00
2025-09-11 15:08:57 +02:00
2026-01-15 12:41:28 +03:00
2026-02-23 12:18:44 +01:00
2025-10-06 13:58:37 +02:00
2025-06-20 13:16:55 -06:00
2023-01-13 12:27:21 +01:00
2026-02-03 14:10:49 -06:00
2025-07-02 15:05:42 +00:00
2025-07-28 15:02:26 +01:00
2026-02-03 09:45:23 +00:00
2026-02-27 16:50:56 +00:00
2025-02-27 06:23:18 -05:00
2023-01-29 15:47:24 -06:00
2025-05-29 08:55:19 -05:00
2025-06-20 13:16:55 -06:00
2026-02-03 14:10:49 -06:00
2025-10-27 17:14:16 -06:00
2025-09-26 11:43:32 +02:00
2024-02-14 17:14:49 +00:00
2026-02-13 19:44:50 -05:00
2024-07-24 14:11:29 +10:00
2026-01-08 15:24:11 +04:00
2025-08-19 10:41:33 +02:00
2025-06-20 13:16:55 -06:00
2022-08-25 19:10:42 +03:00
2023-01-29 15:47:24 -06:00
2024-09-10 20:55:50 +10:00
2026-02-03 12:48:25 -06:00
2024-12-19 00:51:30 +04:00
2024-12-19 00:51:30 +04:00
2025-06-16 16:15:59 -06:00
2026-02-06 15:52:54 +00:00
2026-02-06 15:52:54 +00:00
2025-09-10 11:01:54 -05:00
2026-02-17 15:11:04 +00:00
2025-06-20 13:16:55 -06:00
2022-11-08 17:12:06 +00:00
2026-01-08 15:24:11 +04:00
2026-02-19 13:04:53 -05:00
2026-01-14 09:47:50 -08:00