Runs
Retries
Network errors and busy models are retried with growing waits between tries. A run is never started twice, because each request carries an idempotency key.
What is retried
The SDKs retry a request when it fails for a reason that may pass: a dropped connection, a timeout, a 429, or a 5xx. They don't retry a 4xx that will fail the same way again, like a bad key.
Waits double each time, with up to 25% jitter, so a thousand clients that fail together don't all come back together.
| Try | Wait before it |
|---|---|
| 1 | none |
| 2 | about 1 s |
| 3 | about 2 s |
| 4 | about 4 s |
| 5 | about 8 s |
A Retry-After header always wins over the table.
Change it
const kestrel = new Kestrel({ maxRetries: 2 });
// or for one call
await kestrel.runs.create({ repo: "acme/web-app", task: "Bump lodash" }, { maxRetries: 0 });kestrel = Kestrel(max_retries=2)
# or for one call
kestrel.runs.create(repo="acme/web-app", task="Bump lodash", max_retries=0)Idempotency keys
A retried POST /runs could start the same task twice. The SDKs send an Idempotency-Key header with each create, and reuse it on every retry, so Kestrel starts the run once and returns the same run to every try.
curl https://api.kestrel.dev/v1/runs \
-H "Authorization: Bearer $KESTREL_API_KEY" \
-H "Idempotency-Key: 4f1c2a8e-ci-build-8812" \
-d '{ "repo": "acme/web-app", "task": "Bump lodash" }'Keys are kept for 24 hours. Use something tied to the job, like a CI build number, so a re-run of the same build doesn't start a second Kestrel run.
Retries inside a run
Inside a sandbox, Kestrel also retries the steps that fail for passing reasons: a model that is busy, or a package registry that times out. Those retries show in the trace as extra tries on one step, not as new steps.
Was this page helpful?