You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: add bounded jittered backoff to StartableToolSet retry path
Fixes issue #4060: RAG semantic-embeddings indexing triggered a
rate-limit retry storm — repeated toolset-start attempts had no pacing
after a 429 from the embedding provider.
Implementation:
- modelerrors.RetryableHTTPStatus(err): HTTP-status classifier that
recognises 429, 408, and 5xx via *StatusError first, then falls back
to statusCodeRegex. The toolset gate pre-filters to *StatusError via
errors.As before calling it, so port numbers and chunk counts in
plain error strings cannot arm the gate.
- pkg/tools/startable_backoff.go: bounded exponential backoff with
additive 0-20% jitter (base=15s, cap=5min, delay∈[d,1.2d]).
- Gate in tryStartLocked (TryStart/TryStartWithTimeout only): blocking
Start() bypasses it so mcpcatalog enable and skill startup are
immediate.
- Gate adopts a live StartReporter after /toolset-restart without
waiting for the window to expire.
- Wrap embedding errors in WrapHTTPError at openai/client.go and
dmr/embed.go so a 429 from the embedding provider surfaces as
*StatusError and correctly arms the gate.
- WithStartRetryJitter / WithStartRetryClock options via variadic
NewStartable for deterministic test control.
- Stale 'retry on next turn' log messages updated in agent.go/mcp.go.
- Partial-start exemption documented (code-mode composites remain
unpaced; follow-up at issue #4067).
Tests (same commit, covering the above):
- startable_backoff_test.go: unit tests for the gate (gate fires on
429/408/5xx StatusError, not on plain text / context errors,
blocking Start() ungated, concurrency, jitter bounds)
- startable_backoff_regression_test.go: consumer-shaped regression
suite (RAG/MCP/LSP error shapes, no-goroutine/timer leak, latch)
- rag_backoff_test.go: real-toolset integration test via rag.New +
fake clock
Docs:
- docs/tools/rag/index.md: 'Indexing failures, retries and backoff'
section with trigger table, parameters, and troubleshooting.
- docs/tools/mcp/index.md, docs/tools/lsp/index.md: lifecycle notes
confirming local startup failures fail fast.
Scope: DefaultStartTimeout (30s) unchanged — deferred.
Copy file name to clipboardExpand all lines: docs/community/troubleshooting/index.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -199,6 +199,8 @@ MCP tools using stdio transport must complete the initialization handshake befor
199
199
200
200
If a toolset keeps crashing in a tight loop, tune the [`lifecycle`](../../configuration/tools/index.md#toolset-lifecycle) block on the toolset (e.g. raise `backoff.initial`, lower `max_restarts`, or switch to the `best-effort` profile) so a flaky dependency does not amplify into a restart storm.
201
201
202
+
If a **RAG knowledge base** is failing to index because the embedding provider is rate-limiting requests (HTTP 429), Docker Agent automatically backs off and retries — see [Indexing failures, retries and backoff](../../tools/rag/index.md#indexing-failures-retries-and-backoff) for the retry schedule and the `max_indexing_concurrency` / `max_embedding_concurrency` knobs that control how much concurrent load is generated.
Copy file name to clipboardExpand all lines: docs/tools/lsp/index.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -197,6 +197,8 @@ Available Capabilities:
197
197
198
198
LSP toolsets are managed by the same supervisor as MCP toolsets, so a crashed `gopls` (or any other language server) is reconnected automatically with exponential backoff. Use the [`lifecycle`](../../configuration/tools/index.md#toolset-lifecycle) block to tune the policy per toolset — for example, mark `gopls` as `strict` if your CI flow requires it to be available, or use `/toolset-restart gopls` from the TUI to force a reconnect when the server gets stuck.
199
199
200
+
**Startup failure behaviour:** local LSP server startup failures (missing binary, server-unavailable) fail fast — each turn retries immediately with no artificial delay. The rate-limit backoff gate applies only to model-provider embedding calls (see [Indexing failures, retries and backoff](../rag/index.md#indexing-failures-retries-and-backoff)); it does not apply to LSP server startup.
Copy file name to clipboardExpand all lines: docs/tools/mcp/index.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -258,6 +258,8 @@ toolsets:
258
258
259
259
See [Toolset Lifecycle](../../configuration/tools/index.md#toolset-lifecycle) for all profiles and tuning knobs, and [`/toolset-restart`](../../features/tui/index.md) to force a reconnect from the TUI.
260
260
261
+
**Startup failure behaviour:** local MCP startup failures (missing binary, connection refused, authentication error) fail fast — each turn retries immediately with no artificial delay. The rate-limit backoff gate applies only to model-provider embedding calls (see [Indexing failures, retries and backoff](../rag/index.md#indexing-failures-retries-and-backoff)); it does not apply to MCP server startup.
0 commit comments