Airflow Sensors Exposed: 4,320 Pokes and No File Found
Airflow FileSensor poked 4,320 times holding a worker slot.
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
- ✓Basic DAG authoring with TaskFlow
- ✓Understanding of worker slots and Pools
- ✓Access to a landing bucket or SFTP drop
- Airflow sensors are operators that wait for an event like a file, table, or upstream DAG before letting downstream run
- Key components are poke_interval, timeout, mode poke vs reschedule, soft_fail, and deferrable operators with the triggerer
- Performance insight: poke mode held a worker slot for 12 hours across 4,320 pokes; reschedule mode frees the slot between checks
- Production insight: always set timeout and prefer reschedule or deferrable for waits over 5 minutes to avoid slot starvation
A sensor is like waiting by the door for a package instead of going about your day and checking every hour. If you stand by the door all day you cannot do anything else, but if you check and go back to work you stay productive. Airflow sensors work the same way: poke mode stands by the door holding a worker, while reschedule mode checks and frees the worker until the next check.
Your FileSensor poked 4,320 times overnight and the file never arrived. It also held a worker slot the whole time, so real tasks queued behind a wait.
Sensors are not free. You'll learn when to poke, when to reschedule, and when to defer so waits stop costing you workers.
We cover the sensor catalog, timeout math, and the custom sensor pattern that keeps prod calm. You'll set timeouts on every sensor after this.
Waiting should be cheap. Make it so.
What a Sensor Is: An Operator That Waits
A sensor succeeds when its condition appears and fails when its timeout expires. Until then it pokes on an interval you control.
Use sensors for files, partitions, upstream DAGs, and API readiness. Do not use them as polling ETL loops; they wait, they do not extract.
Poke vs Reschedule: Worker Slot Economics
Poke holds a worker slot for the entire wait and sleeps between checks. Reschedule frees the slot between checks and re-queues the poke.
Short waits under a minute can poke. Anything longer should reschedule. A 12-hour poke at 10-second intervals burned 4,320 slot-minutes for nothing.
Latency decides the mode: checks every few seconds belong in poke, checks every minute or slower belong in reschedule. Reschedule with a poke_interval under about 5 minutes can swamp the scheduler with rapid re-queues, so lengthen the interval instead of shrinking it. For waits past an hour, add deferrable True where the sensor supports it and the Triggerer watches while workers stay free. Set exponential_backoff True with max_wait to back off polling against flaky vendors instead of hammering them at a fixed cadence.
Timeout and Soft Fail: Fail vs Skip vs Keep Waiting
timeout caps the total wait from first poke to deadline. When it expires the sensor fails unless soft_fail is true, which marks it skipped.
Use fail for money-path inputs where missing data must page. Use soft_fail for optional partitions where downstream should skip gracefully.
The stock timeout is seven days, which is a trap; always set an explicit deadline per dataset. timeout counts from the first poke attempt, not per retry, and retries restart parts of that accounting, so a sensor with generous retries can outlive its timeout across tries. Don't confuse timeout with execution_timeout: in reschedule mode each poke attempt is short, so execution_timeout barely bites while timeout governs the whole wait. Two more failure flavors exist: silent_fail logs poke exceptions and keeps waiting, while soft_fail converts deadline misses to skips. A proven pattern pairs soft_fail with a BranchPythonOperator on ALL_DONE: the sensor skips on timeout, the branch reads the sensor state and routes to a with-data or without-data path.
The Common Sensor Catalog
FileSensor watches landing paths. S3KeySensor watches object keys. ExternalTaskSensor waits on another DAG run. TimeSensor waits until a clock time.
SqlSensor polls a row count or watermark. HttpSensor checks API readiness. Pick the sensor that matches the event instead of shelling out in Bash.
Rough starting points that survive prod: FileSensor at 60s poke with a 2 to 4 hour timeout, ExternalTaskSensor at 300s poke with 6 to 12 hours, HttpSensor at 30 to 60s with 30 minutes to 2 hours, S3KeySensor at 60s with 2 to 4 hours, SqlSensor at 30s poke with 30 minutes to an hour. Prefer PythonSensor over BashSensor for JSON or auth logic, and TimeDeltaSensor over sleep calls so waits stay visible in the UI. HttpSensor needs its own request timeout separate from the sensor timeout or one hung socket eats the whole deadline.
ExternalTaskSensor and Its Exactness Gotcha
ExternalTaskSensor needs the exact logical date of the upstream run. A one-hour delta mismatch means it waits forever on a run that already succeeded.
Pin external_dag_id, allowed_states, and execution_delta carefully. When producers and consumers drift, switch to asset-triggered scheduling instead of date math.
Write a Custom Sensor
Subclass BaseSensorOperator and implement poke() returning True or False. Return False for not-ready, True for ready, and raise only on auth failures.
Catch transient network errors inside poke and return False so retries handle blips. Keep poke fast under 30 seconds and idempotent across calls.
Modern Airflow also offers @task.sensor, which turns a Python function into a sensor returning PokeReturnValue(is_done=..., xcom_value=...). The xcom_value lands in XCom on success, so the waiter can hand the found partition path straight downstream with zero extra tasks. Keep poke under 30 seconds, catch transient errors and return False, and reserve raises for auth failures.
The FileSensor That Poked 4,320 Times and Starved the Workers
- Never use poke mode for waits over 5 minutes; reschedule or defer instead — don't hold a slot all night for one file.
- Every sensor needs an explicit timeout and a decision on soft_fail.
- Alert on sensor age, not just sensor failure, to catch silent vendor delays.
airflow tasks logs ledger_daily wait_vendor_file 1 --tail 100ls -lh /data/landing/vendor_ledger.csv && date -u| File | Command / Code | Purpose |
|---|---|---|
| dags | from airflow.decorators import dag, task | What a Sensor Is |
| dags | from airflow.decorators import dag, task | Timeout and Soft Fail |
| dags | from airflow.decorators import dag, task | ExternalTaskSensor and Its Exactness Gotcha |
Key takeaways
Common mistakes to avoid
4 patternsUsing poke mode for overnight waits
Leaving timeout unset
Mismatched dates on ExternalTaskSensor
Raising inside poke on transient errors
Interview Questions on This Topic
What is the difference between poke and reschedule mode?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.
That's Airflow. Mark it forged?
3 min read · try the examples if you haven't