Skip to main content
Workflows can fail for many reasons: API downtime, invalid data, network issues, or bugs. Proper error handling ensures your workflows are resilient and can recover from failures gracefully.

Understanding Flow Failures

When a step in your workflow fails, the default behavior is to stop execution:
// From packages/shared/src/lib/automation/flow-run/execution/flow-execution.ts
export enum FlowRunStatus {
  RUNNING = 'RUNNING',
  SUCCEEDED = 'SUCCEEDED',
  FAILED = 'FAILED',
  PAUSED = 'PAUSED',
  STOPPED = 'STOPPED'
}
A failed step causes the entire workflow to stop with status FAILED, and subsequent steps don’t execute.

Error Handling Options

Every action step in Activepieces supports error handling configuration:
// From packages/shared/src/lib/automation/flows/actions/action.ts
export const ActionErrorHandlingOptions = {
  "continueOnFailure": {
    "value": boolean  // Continue flow even if step fails
  },
  "retryOnFailure": {
    "value": boolean  // Retry step on failure
  }
}

Configure Error Handling

1

Select a Step

Click on any action step in your workflow.
2

Open Error Handling

In the step configuration panel, find the Error Handling section.
3

Enable Options

Toggle the options you need:
  • Continue on Failure: Flow continues even if this step fails
  • Retry on Failure: Automatically retry the step before failing

Continue on Failure

When enabled, the workflow continues executing even if the step fails:
// From packages/server/engine/test/handler/flow-error-handling.test.ts
{
  "name": "risky_operation",
  "type": "CODE",
  "settings": {
    "sourceCode": {
      "code": "export const code = async () => { throw new Error('Intentional error'); }"
    },
    "errorHandlingOptions": {
      "continueOnFailure": {
        "value": true  // Workflow continues
      },
      "retryOnFailure": {
        "value": false
      }
    }
  }
}

// Result:
{
  "verdict": {
    "status": "RUNNING"  // Flow continues!
  },
  "steps": {
    "risky_operation": {
      "status": "FAILED",
      "errorMessage": "Custom Runtime Error"
    }
  }
}

When to Use

Optional Operations

Non-critical steps like logging, analytics, or notifications.

Fallback Logic

When you have alternative steps to handle failures.

Best Effort

Operations that should try but not block the workflow.

Parallel Operations

When processing multiple items and some can fail.

Example: Optional Notification

{
  "name": "send_slack_notification",
  "type": "PIECE",
  "settings": {
    "pieceName": "@activepieces/piece-slack",
    "actionName": "send_message",
    "input": {
      "channel": "#notifications",
      "text": "Order processed: {{ trigger.orderId }}"
    },
    "errorHandlingOptions": {
      "continueOnFailure": {
        "value": true  // Don't fail workflow if Slack is down
      }
    }
  },
  "nextAction": {
    "name": "update_database",  // This still runs even if Slack fails
    "type": "PIECE",
    "settings": { /* ... */ }
  }
}

Retry on Failure

Automatically retry a step before marking it as failed:
{
  "name": "api_call",
  "type": "PIECE",
  "settings": {
    "pieceName": "@activepieces/piece-http",
    "actionName": "send_request",
    "input": {
      "method": "GET",
      "url": "https://api.example.com/data"
    },
    "errorHandlingOptions": {
      "retryOnFailure": {
        "value": true  // Retry on failure
      }
    }
  }
}
Retries use exponential backoff to avoid overwhelming failing services.

When to Use

API calls that might fail due to temporary network issues.
// HTTP requests, webhooks, external API calls
{
  "errorHandlingOptions": {
    "retryOnFailure": { "value": true }
  }
}
Services that might return rate limit errors.
// APIs with rate limits
{
  "pieceName": "@activepieces/piece-openai",
  "errorHandlingOptions": {
    "retryOnFailure": { "value": true }
  }
}
Database queries that might face temporary connection issues.
// Database connections
{
  "pieceName": "@activepieces/piece-postgresql",
  "errorHandlingOptions": {
    "retryOnFailure": { "value": true }
  }
}
File uploads or downloads that might be interrupted.
// File operations
{
  "actionName": "upload_file",
  "errorHandlingOptions": {
    "retryOnFailure": { "value": true }
  }
}

Combining Error Handling Options

You can enable both options for maximum resilience:
{
  "name": "best_effort_api_call",
  "type": "PIECE",
  "settings": {
    "pieceName": "@activepieces/piece-http",
    "actionName": "send_request",
    "errorHandlingOptions": {
      "retryOnFailure": {
        "value": true  // Try multiple times
      },
      "continueOnFailure": {
        "value": true  // But don't block workflow if all retries fail
      }
    }
  }
}

Error Detection Patterns

Check Step Status

In subsequent steps, check if a previous step failed:
{
  "name": "check_previous_step",
  "type": "ROUTER",
  "settings": {
    "conditions": [[
      {
        "firstValue": "{{ api_call.status }}",
        "operator": "TEXT_EXACTLY_MATCHES",
        "secondValue": "SUCCEEDED"
      }
    ]]
  },
  "children": [
    // Branch 1: API succeeded
    {
      "name": "process_data",
      "settings": {
        "input": {
          "data": "{{ api_call.body }}"
        }
      }
    },
    // Branch 2: API failed, use fallback
    {
      "name": "use_cached_data",
      "settings": {
        "input": {
          "data": "{{ cached_data.output }}"
        }
      }
    }
  ]
}

Handle Missing Data

Use null coalescing for safe data access:
{
  "input": {
    // Provide defaults if step failed
    "userData": "{{ get_user.body ?? { name: 'Unknown', email: 'none' } }}",
    "count": "{{ fetch_count.output ?? 0 }}"
  }
}

Error Notification Patterns

Send Alert on Failure

{
  "name": "critical_operation",
  "type": "PIECE",
  "settings": { /* ... */ },
  "nextAction": {
    "name": "check_if_failed",
    "type": "ROUTER",
    "settings": {
      "conditions": [[
        {
          "firstValue": "{{ critical_operation.status }}",
          "operator": "TEXT_EXACTLY_MATCHES",
          "secondValue": "FAILED"
        }
      ]]
    },
    "children": [
      // Send alert if failed
      {
        "name": "send_alert",
        "type": "PIECE",
        "settings": {
          "pieceName": "@activepieces/piece-gmail",
          "actionName": "send_email",
          "input": {
            "to": "admin@example.com",
            "subject": "Critical Operation Failed",
            "body": "Error: {{ critical_operation.errorMessage }}"
          }
        }
      },
      // Continue normally if succeeded
      null
    ]
  }
}

Log All Errors

Create a dedicated error logging step:
{
  "name": "log_errors",
  "type": "CODE",
  "settings": {
    "sourceCode": {
      "code": `
export const code = async (inputs) => {
  const errors = [];
  
  // Check each step for failures
  for (const [stepName, stepData] of Object.entries(inputs.steps)) {
    if (stepData.status === 'FAILED') {
      errors.push({
        step: stepName,
        error: stepData.errorMessage,
        timestamp: new Date().toISOString()
      });
    }
  }
  
  // Log to external service
  if (errors.length > 0) {
    await fetch('https://logging.example.com/errors', {
      method: 'POST',
      body: JSON.stringify({ flowId: inputs.flowId, errors })
    });
  }
  
  return errors;
};
      `
    },
    "input": {
      "steps": "{{ $steps }}",  // All step results
      "flowId": "{{ $flow.id }}"
    }
  }
}

Flow-Level Error Handling

Failed Step Information

// From packages/shared/src/lib/automation/flow-run/flow-run.ts
export type FlowRun = {
  status: FlowRunStatus,
  steps: Record<string, StepOutput>,
  failedStep?: {
    name: string,
    displayName: string,
    message: string
  }
}
When a flow fails, you can see which step caused the failure:
{
  "status": "FAILED",
  "failedStep": {
    "name": "send_http",
    "displayName": "Send HTTP Request",
    "message": "Request failed with status 404"
  }
}

Retry Entire Flow

// From packages/shared/src/lib/automation/flow-run/flow-run.ts
export enum FlowRetryStrategy {
  ON_LATEST_VERSION = 'ON_LATEST_VERSION',      // Retry with latest flow version
  FROM_FAILED_STEP = 'FROM_FAILED_STEP'         // Resume from failed step
}
From the flow runs page, click Retry on a failed run.

Error Handling in Loops

When a step fails inside a loop:
// From packages/server/engine/test/handler/flow-looping.test.ts
{
  "name": "bulk_process",
  "type": "LOOP_ON_ITEMS",
  "settings": {
    "items": "{{ [4, 5, 6] }}"
  },
  "firstLoopAction": {
    "name": "process_item",
    "type": "CODE",
    "settings": {
      "sourceCode": {
        "code": "export const code = async () => { throw new Error('Failed'); }"
      },
      "errorHandlingOptions": {
        "continueOnFailure": {
          "value": false  // Stop loop on first failure
        }
      }
    }
  }
}

// Result: Loop stops at first iteration
{
  "output": {
    "iterations": [
      {
        "index": 1,
        "item": 4,
        "process_item": {
          "status": "FAILED",
          "errorMessage": "Failed"
        }
      }
    ],
    "index": 1,
    "item": 4
  }
}

Continue Loop on Errors

To process all items even if some fail:
{
  "firstLoopAction": {
    "name": "process_item",
    "settings": {
      "errorHandlingOptions": {
        "continueOnFailure": {
          "value": true  // Continue with next iteration
        }
      }
    }
  }
}

Best Practices

Always enable retry for external API calls that might be temporarily unavailable.
{
  "errorHandlingOptions": {
    "retryOnFailure": { "value": true }
  }
}
Only enable for truly optional steps. Critical operations should fail the workflow.
// Good: Optional analytics
{ "continueOnFailure": true }

// Bad: Critical database update
{ "continueOnFailure": false }
Check data validity before expensive operations.
{
  "name": "validate_input",
  "type": "CODE",
  "settings": {
    "sourceCode": {
      "code": `
export const code = async (inputs) => {
if (!inputs.email || !inputs.email.includes('@')) {
throw new Error('Invalid email format');
}
return inputs;
};
      `
    }
  }
}
Create a logging step that runs even on failures.
{
  "name": "log_execution",
  "errorHandlingOptions": {
    "continueOnFailure": { "value": true }
  }
}
Test your workflows with:
  • Invalid data
  • Network timeouts
  • API errors
  • Missing required fields

Common Error Scenarios

HTTP 404 Errors

// From packages/server/engine/test/handler/flow-error-handling.test.ts
{
  "send_http": {
    "status": "FAILED",
    "errorMessage": JSON.stringify({
      "response": {
        "status": 404,
        "body": {
          "statusCode": 404,
          "error": "Not Found",
          "message": "Route not found"
        }
      }
    }, null, 2)
  }
}

Runtime Errors in Code

{
  "runtime": {
    "status": "FAILED",
    "errorMessage": "Custom Runtime Error: Cannot read property 'name' of undefined"
  }
}

Connection Errors

{
  "database_query": {
    "status": "FAILED",
    "errorMessage": "Connection timeout: Could not connect to database"
  }
}

Debugging Failed Runs

1

View Run History

Navigate to the Runs tab to see all executions.
2

Click Failed Run

Click on a run with status FAILED.
3

Identify Failed Step

The failed step is highlighted with error details.
4

Review Error Message

Read the error message to understand what went wrong.
5

Check Step Input

Verify the input data that caused the failure.
6

Fix and Retry

Update the flow or data, then retry the run.

Next Steps

Debugging

Learn how to debug workflows

Monitoring

Monitor workflow health

Loops & Branches

Handle errors in control flow

Best Practices

Build resilient workflows