Winix.Retry
0.4.0
Prefix Reserved
dotnet tool install --global Winix.Retry --version 0.4.0
dotnet new tool-manifest
dotnet tool install --local Winix.Retry --version 0.4.0
#tool dotnet:?package=Winix.Retry&version=0.4.0
nuke :add-package Winix.Retry --version 0.4.0
retry
Run a command with automatic retries on failure. Configurable retry count, delay, backoff strategy, and exit-code filtering.
A drop-in replacement for ad-hoc shell retry loops — more robust, readable, and cross-platform.
No native retry command exists on any platform — retry fills that gap everywhere.
Install
Scoop (Windows)
scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/retry
Winget (Windows, stable releases)
winget install Winix.Retry
.NET Tool (cross-platform)
dotnet tool install -g Winix.Retry
Direct Download
Download native binaries from GitHub Releases.
Usage
retry [options] [--] <command> [args...]
Examples
# Retry flaky tests (3 retries, 1s delay)
retry dotnet test
# More retries with longer delay
retry --times 5 --delay 2s dotnet test
# API health check with exponential backoff and jitter
retry --times 5 --delay 1s --backoff exp --jitter curl -f http://api/health
# Poll until Docker is ready (retry until exit code 0)
retry --until 0 --delay 5s docker ps
# Wait until a service port accepts connections (wait-for-it.sh replacement)
retry --until 0 --times 30 --delay 2s nc -z localhost 5432
# Retry only on specific exit codes
retry --on 1,2 --times 3 make build
# Time the entire retry sequence
timeit retry make test
# File-watch with auto-retry on change
peep -- retry --times 2 make test
Output
Default (stderr):
retry: attempt 1/4 failed (exit 1), retrying in 1s...
retry: attempt 2/4 failed (exit 1), retrying in 1s...
retry: attempt 3/4 failed (exit 1), retrying in 1s...
retry: attempt 4/4 failed (exit 1), no retries remaining
JSON (--json, stderr):
{"tool":"retry","version":"0.2.0","exit_code":1,"exit_reason":"retries_exhausted","child_exit_code":1,"attempts":4,"max_attempts":4,"total_seconds":3.012,"delays_seconds":[1.000,1.000,1.000]}
Options
| Option | Short | Default | Description |
|---|---|---|---|
--times N |
-n N |
3 |
Max retry attempts (not counting initial run) |
--delay D |
-d D |
1s |
Delay before retries (e.g. 500ms, 2s, 1m) |
--backoff S |
-b S |
fixed |
Backoff strategy: fixed, linear, exp |
--jitter |
off | Add random jitter (50–100% of computed delay) | |
--on X,Y |
(none) | Retry only on these exit codes | |
--until X,Y |
(none) | Stop retrying when exit code matches | |
--stdout |
off | Write summary to stdout instead of stderr | |
--json |
off | JSON output | |
--color[=auto\|always\|never] |
auto | Coloured output: auto (default when omitted), always, or never. | |
--no-color |
auto | Disable coloured output | |
--version |
Show version | ||
--help |
-h |
Show help | |
--describe |
AI/agent metadata (JSON) |
Delay Format
Delays accept a number followed by a unit suffix: ms (milliseconds), s (seconds), m (minutes). Examples: 500ms, 2s, 1m, 90s.
Backoff Strategies
| Strategy | Behaviour |
|---|---|
fixed |
Same delay between every retry |
linear |
Delay increases by the base delay each retry (1×, 2×, 3×, …) |
exp |
Delay doubles each retry (1×, 2×, 4×, 8×, …) |
Add --jitter to any strategy to randomise the delay within 50–100% of the computed value — useful for avoiding thundering-herd when multiple processes retry simultaneously.
Exit-Code Filtering
--on and --until let you control exactly when retries happen:
--on 1,2— only retry if the exit code is 1 or 2; pass through any other exit code immediately--until 0— keep retrying until the exit code matches (poll mode)- These flags accept comma-separated lists of integers
In poll mode, remember the default is --times 3 — polling gives up after 4 attempts (~6 seconds with defaults). Set --times explicitly to your wait budget: total wait ≈ --times × --delay. On exhaustion the child's last exit code passes through, so scripts fail honestly instead of proceeding. Combined with nc -z this replaces wait-for-it.sh cross-platform:
# Up to 60s for Postgres to start listening, then run migrations
retry --until 0 --times 30 --delay 2s nc -z localhost 5432 && dotnet ef database update
Exit Codes
retry passes through the child process's exit code (0–124) on success, exhaustion, or non-retryable result. When retry itself fails:
| Code | Meaning |
|---|---|
| 125 | Usage error — no command, bad retry arguments, empty --on/--until list |
| 126 | Command found but not executable (permission denied, bad EXE format) |
| 127 | Command not found on PATH |
Empty --on or --until lists (e.g. --on "" or --on ",,,") are rejected as usage errors, not silently treated as "no filter". Pass retry CMD without the flag if you want no filter.
Cancellation
Press Ctrl+C to interrupt the retry loop. retry forcibly terminates the child with Process.Kill(entireProcessTree: true) — SIGKILL on Unix, TerminateProcess on Windows. This is non-graceful: the child's SIGTERM handlers are NOT invoked. Retry then waits up to 5 seconds for the child to exit before exiting itself.
Exit codes:
- Child's exit code (usually 130 on Unix if the child handled the signal, otherwise a kill-signal value) — retry passes through whatever the child returned when it exited within the 5-second grace window. It does NOT substitute a fixed 130.
137— the child ignored the kill and retry abandoned the wait after 5 seconds. The child may still be running as an orphan — retry logs a warning to stderr with the PID. If the child holds locks or ports, subsequent commands may fail until you manually terminate it.
Most well-behaved commands exit promptly on Ctrl+C; the orphan scenario is unusual (typically daemons or commands that install custom signal handlers).
Colour
- Automatic: colour when outputting to a terminal, plain when piped
--colorforces colour on (overridesNO_COLOR)--no-colorforces colour off- Respects the
NO_COLORenvironment variable (no-color.org)
Part of Winix
retry is part of the Winix CLI toolkit.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
## [0.3.0] - 2026-05-10
- Initial release.
See full changelog at https://github.com/Yortw/winix/blob/main/src/retry/CHANGELOG.md