Apache Airflow: From Basics to Mastery
No workflow runs perfectly every time. A network request may fail, a database might be down, or a file might not arrive on time. Apache Airflow provides built-in error handling and retry mechanisms to make your pipelines resilient and reliable.
In this lesson, you’ll learn how Airflow handles task failures, how to configure retries, and how to implement best practices for robust pipelines.
Learning Objectives
By the end of this lesson, you will be able to:
Understand how Airflow detects and reports task failures.
Configure retries and delays when tasks fail.
Use callbacks and alerts to notify your team of failures.
Apply best practices for handling errors gracefully.
1. How Airflow Handles Failures
Each task run can either: success, fail, skip, or be up for retry.
If a task fails, Airflow logs the error and stops downstream tasks (unless configured otherwise).
Failed tasks are highlighted in red in the Web UI.
2. Configuring Retries
Retries are controlled in the task parameters.
Example:
from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime, timedelta
with DAG(
dag_id="retry_example",
start_date=datetime(2025, 1, 1),
schedule_interval="@daily",
catchup=False
) as dag:
task = BashOperator(
task_id="unstable_task",
bash_command="exit 1", # fails intentionally
retries=3, # retry up to 3 times
retry_delay=timedelta(minutes=5) # wait 5 min between retries
)
Â
Here:
The task will retry 3 times if it fails.
Each retry is delayed by 5 minutes.
If all retries fail, the task is marked as failed.
3. Exponential Backoff
Instead of fixed retry delays, you can enable exponential backoff.
task = BashOperator(
task_id="unstable_task",
bash_command="exit 1",
retries=5,
retry_delay=timedelta(minutes=1),
retry_exponential_backoff=True,
max_retry_delay=timedelta(minutes=30)
)
👉 Here, retries happen with increasing delays (1 min → 2 min → 4 min … up to 30 min).
4. Error Handling with Callbacks
You can set callbacks to run functions when tasks fail or succeed.
def notify_failure(context):
print(f"Task {context['task_instance'].task_id} failed.")
task = BashOperator(
task_id="unstable_task",
bash_command="exit 1",
retries=2,
on_failure_callback=notify_failure
)
Best practice
Send notifications via Slack, Email, or PagerDuty for production workflows.
5. Skipping and Handling Failures Gracefully
Use the
trigger_ruleparameter to control how downstream tasks behave.Example: If one upstream task fails but you still want to continue:
from airflow.operators.dummy import DummyOperator
task1 = BashOperator(task_id="task1", bash_command="exit 1")
task2 = BashOperator(task_id="task2", bash_command="echo 'Task 2'")
final = DummyOperator(task_id="final", trigger_rule="all_done")
task1 >> final
task2 >> final
Here, final runs even if task1 fails because trigger_rule="all_done".
6. Best Practices
Always set a reasonable retry limit (avoid infinite loops).
Use exponential backoff for unstable systems.
Add notifications for failures.
Use
trigger_rulecarefully to prevent skipped tasks from blocking pipelines.Avoid retrying tasks that are not idempotent (e.g., tasks that duplicate data).
Finish Course Early?
You have not completed all required lessons and assessments.