Retries
When a handler throws an error, Synkro can automatically retry it according to a RetryConfig.
RetryConfig
type RetryConfig = { maxRetries: number; delayMs?: number; // Default: 1000 backoff?: "fixed" | "exponential"; jitter?: boolean; retryable?: (error: unknown) => boolean;};| Field | Description |
|---|---|
maxRetries | Maximum number of retry attempts (not counting the initial attempt). |
delayMs | Base delay between retries in milliseconds. Defaults to 1000. |
backoff | "fixed" uses the same delay every time. "exponential" doubles the delay on each attempt (delayMs * 2^attempt). |
jitter | When true, randomizes the delay by +/-50% to avoid thundering herd. |
retryable | A function that receives the error and returns false to skip retrying (e.g., for validation errors that will never succeed). |
Backoff strategies
Every retry waits the same amount of time:
Attempt 1 → wait 1000msAttempt 2 → wait 1000msAttempt 3 → wait 1000ms{ maxRetries: 3, delayMs: 1000, backoff: "fixed" }Each retry doubles the wait time:
Attempt 1 → wait 1000msAttempt 2 → wait 2000msAttempt 3 → wait 4000ms{ maxRetries: 3, delayMs: 1000, backoff: "exponential" }When jitter: true, the computed delay is multiplied by a random factor between 0.5 and 1.0, preventing multiple failing handlers from retrying in lockstep.
Per-event retries
synkro.on( "payment:charge", async (ctx) => { await chargeCard(ctx.payload); }, { maxRetries: 5, delayMs: 500, backoff: "exponential", jitter: true },);Or in the declarative config:
const synkro = await Synkro.start({ transport: "in-memory", events: [ { type: "payment:charge", handler: async (ctx) => { await chargeCard(ctx.payload); }, retry: { maxRetries: 5, delayMs: 500, backoff: "exponential" }, }, ],});Per-step retries
Workflow steps accept the same retry config:
{ name: "ProcessOrder", steps: [ { type: "ChargePayment", handler: chargePayment, retry: { maxRetries: 3, backoff: "exponential", delayMs: 1000 }, }, { type: "FulfillOrder", handler: fulfillOrder, retry: { maxRetries: 2, backoff: "fixed", delayMs: 2000 }, }, ],}Skipping non-retryable errors
Use the retryable function to bail out early for errors that will never succeed:
{ maxRetries: 5, retryable: (error) => { if (error instanceof ValidationError) return false; if (error instanceof AuthenticationError) return false; return true; // retry everything else },}