Retry a command on failure
Re-run a command until it succeeds or a retry cap is hit — useful for flaky network calls and CI race conditions.
How to retry a command on failure in each shell
until command; do sleep 2; doneuntil command; do sleep 2; donewhile not command; sleep 2; enddo { try { command; break } catch { Start-Sleep 2 } } while ($true)PowerShell `command` (native or cmdlet) that fails doesn't throw by default — you have to inspect `$LASTEXITCODE` (native) or `$?` (cmdlet success). `Invoke-WebRequest` / `Invoke-RestMethod` DO throw on non-2xx, so the `try/catch` pattern works directly. For native commands wrap: `if ($LASTEXITCODE -eq 0) { break }`.
:retry & command || (timeout /t 2 /nobreak >nul & goto retry)cmd has no native loop construct that's a clean fit — use labels + `goto`. In a `.bat` file: `:retry` + `command` + `if errorlevel 1 (timeout /t 2 /nobreak >nul & goto retry)`. Beware `&` is sequential (run-both-regardless) vs `&&` is conditional (run-second-only-if-first-succeeded).
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- Capped retries (preferred to unbounded `until`): `for i in 1 2 3 4 5; do command && break; sleep $((i*i)); done` — 5 attempts with quadratic backoff (1, 4, 9, 16, 25 seconds, total ~55s). After the loop check `$?` — non-zero means all attempts failed. For exponential backoff: `delay=1; for i in {1..6}; do command && break; sleep $delay; delay=$((delay*2)); done` doubles each time (1, 2, 4, 8, 16, 32).
- `curl --retry N --retry-delay SEC --retry-max-time SEC URL` has BUILT-IN retry on transient HTTP errors (5xx, 408, 429) — no shell loop needed. `--retry-all-errors` (curl 7.71+, 2020) retries on ALL non-2xx not just transient. For load-balanced/CDN endpoints, curl's built-in retry is more robust than a shell loop because it tracks redirects + connection re-use correctly.
- pwsh native cmdlet retry: `Invoke-WebRequest -Uri URL -MaximumRetryCount 5 -RetryIntervalSec 2` (pwsh 7+). 5.1 lacks these flags — wrap manually. Idiom: `for ($i = 0; $i -lt 5; $i++) { try { Invoke-WebRequest -Uri URL; break } catch { Start-Sleep ($i*2+1) } }`. Increments delay each iter.
- Jitter — randomize backoff to avoid thundering-herd on shared services: `sleep $((RANDOM % 5 + delay))`. Critical when many clients retry against the same endpoint simultaneously (every client backing off the same 2s, 4s, 8s causes synchronized retries that just re-overload the service). AWS SDK / Google SDK clients all implement jittered exponential backoff for this reason.
Related commands
Related tasks
- Run a command every N seconds— Repeat a command on a fixed-interval polling loop — useful for live dashboards, healthcheck-watching, and tail-style log monitoring.
- Check if a URL is reachable— Test whether a URL returns 2xx/3xx — useful for healthchecks, wait-for-it scripts, and CI smoke tests.
- Send an HTTP POST request— POST a JSON body (or form fields, or a file) to a URL and capture the response.
- Run a command in the background— Launch a long-running command without blocking the current shell session.