Skip to content

# Retrying Steps

Configure retry behavior based on step reliability characteristics. Set conservative flow-level defaults and override per-step as needed.

Tip

For scheduling delays between steps, see Delaying Steps.

For detailed information about each configuration option, see the Step Execution Options reference.

## Understanding Failure Types

Section titled “Understanding Failure Types”

Not all failures should retry. Understanding the difference helps you configure appropriate retry policies.

### Transient Failures

Section titled “Transient Failures”

Temporary problems that might succeed on retry:

- Network timeouts
- Rate limiting (429 responses)
- Temporary service unavailability (503 responses)
- Database connection issues

Configure with retries:

    .step({  slug: 'fetchExternalData',  maxAttempts: 5,   // Retry transient failures  baseDelay: 2,}, async (input) => await fetchFromAPI(input.run.url))

### Permanent Failures

Section titled “Permanent Failures”

Problems that will never succeed on retry:

- Invalid input format (malformed email, negative numbers)
- Missing required fields
- Business rule violations
- Schema validation errors

Configure without retries:

    .step({  slug: 'validInput',  maxAttempts: 1,   // No retries for validation}, (input) => {  if (!input.run.email) throw new Error('Email required');  return input.run;})

Note

`maxAttempts: 1` means “run once, do not retry”. If the step fails, it fails immediately without retry attempts.

Current Limitation

pgflow does not distinguish between transient and permanent failures automatically. All exceptions trigger retry logic based on `maxAttempts`. Use `maxAttempts: 1` for steps that perform validation or other operations that should fail fast.

For detailed guidance on validation patterns, see Validation Steps.

## Guiding Principle

Section titled “Guiding Principle”

Set conservative flow-level defaults, override per-step as needed.

Most flows contain a mix of operations - some reliable (database writes), some flaky (external APIs), some that should fail fast (validation). Rather than applying aggressive retry policies to all steps, start with sensible defaults and override specific steps based on their failure characteristics.

## Recommended: Per-Step Configuration

Section titled “Recommended: Per-Step Configuration”

When different steps have different reliability requirements:

    new Flow({  slug: 'data_pipeline',  maxAttempts: 3,     // Sensible defaults  baseDelay: 1,})  .step({    slug: 'validate_input',    maxAttempts: 1,   // No retries - validation should not fail  }, validateHandler)  .step({    slug: 'fetch_external',    maxAttempts: 5,   // External API might be flaky    baseDelay: 10,    // Longer delays for external service  }, fetchHandler)  .step({    slug: 'save_results',    // Use flow defaults  }, saveHandler)

Why this approach:

- Set reasonable flow-level defaults
- Override only where needed
- Validation steps need no retries (fail immediately on bad input)
- External API steps need more retries and longer delays
- Simple database operations use defaults

## Learn More

Section titled “Learn More”
Validation Steps Use explicit validation steps to fail fast on invalid input
Step Execution Options Complete reference for all configuration options and their defaults
Delaying Steps Schedule steps to run after specified delays
Worker Configuration Configure Edge Worker concurrency and polling behavior
Tune Deployed Flows Adjust configuration for production flows without redeploying