From 5ceffbc5194972bdd461030a877418060bf0bdf5 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Sat, 14 Mar 2026 16:11:53 -0700 Subject: [PATCH] fix: add exponential backoff to withRetry, bump install retries to 4 (#2634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes Connection reset by peer failures on spotty networks by doubling delay on each retry (10s→20s→40s→80s) and giving installAgent and uploadConfigFile 4 attempts instead of 2. Fixes #2631 Agent: ux-engineer Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- packages/cli/src/shared/agent-setup.ts | 5 +++-- packages/cli/src/shared/ui.ts | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/shared/agent-setup.ts b/packages/cli/src/shared/agent-setup.ts index 0b3677a6..d84d7912 100644 --- a/packages/cli/src/shared/agent-setup.ts +++ b/packages/cli/src/shared/agent-setup.ts @@ -51,7 +51,7 @@ async function installAgent( ): Promise { logStep(`Installing ${agentName}...`); const r = await asyncTryCatch(() => - withRetry(`${agentName} install`, () => wrapSshCall(runner.runServer(installCmd, timeoutSecs)), 2, 10), + withRetry(`${agentName} install`, () => wrapSshCall(runner.runServer(installCmd, timeoutSecs)), 4, 10, true), ); if (!r.ok) { logError(`${agentName} installation failed`); @@ -82,8 +82,9 @@ async function uploadConfigFile(runner: CloudRunner, content: string, remotePath ); })(), ), - 2, + 4, 5, + true, ), ); tryCatchIf(isOperationalError, () => unlinkSync(tmpFile)); diff --git a/packages/cli/src/shared/ui.ts b/packages/cli/src/shared/ui.ts index 2de35f68..cbcfa47f 100644 --- a/packages/cli/src/shared/ui.ts +++ b/packages/cli/src/shared/ui.ts @@ -213,6 +213,7 @@ export async function withRetry( fn: () => Promise>, maxAttempts = 3, delaySec = 5, + exponential = false, ): Promise { for (let attempt = 1; attempt <= maxAttempts; attempt++) { const result = await fn(); // throws → not retried (non-retryable) @@ -222,8 +223,9 @@ export async function withRetry( if (attempt >= maxAttempts) { throw result.error; } - logWarn(`${label} failed (attempt ${attempt}/${maxAttempts}), retrying in ${delaySec}s...`); - await new Promise((r) => setTimeout(r, delaySec * 1000)); + const delay = exponential ? delaySec * 2 ** (attempt - 1) : delaySec; + logWarn(`${label} failed (attempt ${attempt}/${maxAttempts}), retrying in ${delay}s...`); + await new Promise((r) => setTimeout(r, delay * 1000)); } throw new Error("unreachable"); }