Many failures are temporary: an API that answers 503, a database restarting, a lock held a bit too long. Retries let Airflow try again before anyone has to look at the task. A timeout covers the opposite case, a task that hangs and would otherwise hold its worker slot for hours.
Copy and paste
On one task:
from datetime import timedelta
from airflow.sdk import task
@task(
retries=3,
retry_delay=timedelta(minutes=5),
retry_exponential_backoff=2.0,
max_retry_delay=timedelta(minutes=30),
execution_timeout=timedelta(minutes=20),
)
def call_api():
...
On every task of a DAG, plus a limit for the whole run:
from datetime import timedelta
import pendulum
from airflow.sdk import dag
@dag(
schedule="0 6 * * *",
start_date=pendulum.datetime(2026, 1, 1, tz="UTC"),
catchup=False,
default_args={
"retries": 2,
"retry_delay": timedelta(minutes=5),
"execution_timeout": timedelta(hours=1),
},
dagrun_timeout=timedelta(hours=3),
)
def sales_daily():
...
sales_daily()
Classic operators take the same arguments, for example
BashOperator(task_id="export", bash_command="...", retries=3, execution_timeout=timedelta(minutes=20)).
A sensor that waits up to six hours without holding a worker slot:
import os
from airflow.sdk import task
@task.sensor(poke_interval=300, timeout=6 * 60 * 60, mode="reschedule")
def wait_for_file() -> bool:
return os.path.exists("/data/in/sales.csv")
What happens on a timeout
When a task exceeds execution_timeout, Airflow stops it with an AirflowTaskTimeout. A timeout
counts as a failure, so the task is retried if it has retries left:
airflow.sdk.exceptions.AirflowTaskTimeout: Timeout, PID: 98357
Task instance state updated new_state=up_for_retry
dagrun_timeout applies to the whole run: when it expires, the run is marked as failed and the tasks
still running are stopped.
How long the retries wait
With retry_delay=5 minutes and retry_exponential_backoff=2.0, each wait is about twice the
previous one:
| Retry | Wait |
|---|---|
| 1st | 5 to 10 minutes |
| 2nd | 10 to 20 minutes |
| 3rd | 20 to 40 minutes, capped at max_retry_delay (30 minutes above) |
Airflow adds a random but repeatable part to each wait, so that tasks that failed together do not all
retry in the same second. In Airflow 3.3 retry_exponential_backoff is a multiplier: 0, the
default, keeps the delay constant. Older versions took True, which Airflow 3.3 still accepts and
treats as 2.0.
Things to know
- The default is no retries. Without
retries, a task fails at the first error. To change the default for every DAG, setAIRFLOW__CORE__DEFAULT_TASK_RETRIES. - Some errors should not be retried. Raise
AirflowFailException, fromairflow.sdk.exceptions, to fail the task at once, for example on a configuration error that another attempt cannot fix. - Alert on the last failure.
on_failure_callbackruns once the retries are used up. Useon_retry_callbackto hear about every retry; see how to send a Slack message when a task fails. - Sensor timeouts. For a sensor,
timeoutis the total time it may keep checking, across all pokes. Withmode="reschedule"the worker slot is released between checks, which matters when the wait is long.
Tested with Airflow 3.3.2.