Aiki
Guides

Retry Strategies

Aiki provides automatic retry capabilities for both tasks and workflows. This guide helps you choose the right retry strategy for your use case.

Available Strategies

Never (Default)

No automatic retries. The task or workflow fails immediately on error.

retry: { type: "never" }

Use when:

  • Operations that shouldn't be retried (e.g., user input validation)
  • Non-idempotent operations that can't safely be repeated
  • You want manual control over retry logic

Fixed

Retries with a constant delay between attempts.

retry: {
	type: "fixed",
	maxAttempts: 3,
	delayMs: 1000, // 1 second between retries
}

Use when:

  • Simple retry scenarios
  • Internal service calls with predictable recovery times
  • Operations where timing isn't critical

Exponential

Retries with increasing delays (e.g., 1s, 2s, 4s, 8s...).

retry: {
	type: "exponential",
	maxAttempts: 5,
	baseDelayMs: 1000,    // Start with 1 second
	factor: 2,            // Double each time (default)
	maxDelayMs: 30000,    // Cap at 30 seconds (optional)
}

Use when:

  • External API calls that may be rate-limited
  • Network operations with transient failures
  • Services that need time to recover

Jittered

Exponential backoff with randomization to prevent thundering herd problems.

retry: {
	type: "jittered",
	maxAttempts: 5,
	baseDelayMs: 1000,
	jitterFactor: 0.5,    // Add up to 50% random variation (default)
	maxDelayMs: 30000,
}

Use when:

  • High-concurrency scenarios where many workflows might retry simultaneously
  • Shared external resources (databases, APIs)
  • Preventing synchronized retry storms

Task vs Workflow Retry

Both tasks and workflows support retry configuration, but they serve different purposes:

Task Retry

Retries a single unit of work within a workflow.

const sendNotification = task({
	name: "send-notification",
	handler(input) {
		return notificationService.send(input);
	},
	retry: {
		type: "exponential",
		maxAttempts: 3,
		baseDelayMs: 1000,
	},
});
  • Retries only the failed task
  • Other completed tasks in the workflow are not re-executed
  • Good for isolating failures in specific operations

Workflow Retry

Retries the entire workflow from where it failed.

const orderWorkflowV1 = orderWorkflow.v("1.0.0", {
	handler(run, input) {
		// ...
	},
	retry: {
		type: "exponential",
		maxAttempts: 3,
		baseDelayMs: 5000,
	},
});
  • Retries from the point of failure (completed tasks are skipped)
  • Useful when the workflow itself has logic that might fail
  • Catches errors not handled by individual tasks

Idempotency

Tasks with retry must be idempotent - running them multiple times should produce the same result.

Use idempotency keys for operations that have side effects:

const chargeCard = task({
	name: "charge-card",
	handler(input) {
		return paymentProvider.charge({
			amount: input.amount,
			idempotencyKey: input.transactionId, // Prevents duplicate charges
		});
	},
	retry: { type: "exponential", maxAttempts: 3, baseDelayMs: 1000 },
});

See the Determinism Guide for more on writing reliable workflows and tasks.

Next Steps

  • Tasks - Task configuration and execution
  • Workflows - Workflow orchestration
  • Determinism - Workflow determinism and task idempotency

On this page