Skip to content

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;
};
FieldDescription
maxRetriesMaximum number of retry attempts (not counting the initial attempt).
delayMsBase 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).
jitterWhen true, randomizes the delay by +/-50% to avoid thundering herd.
retryableA 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 1000ms
Attempt 2 → wait 1000ms
Attempt 3 → wait 1000ms
{ maxRetries: 3, delayMs: 1000, backoff: "fixed" }

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
},
}