Apache Airflow version
3.3.1
What happened?
When clearing a task instance for a specific Dag run with both include_upstream and include_downstream enabled (the "Upstream" + "Downstream" toggles in the Clear Task Instance dialog, or POST /api/v2/dags/{dag_id}/clearTaskInstances with a dag_run_id), Airflow also selects tasks that are neither upstream nor downstream of the selected task.
The downstream traversal starts from the selected task plus all of its upstream tasks, so it picks up every descendant of those ancestors. In a Dag where all branches hang off a shared root task, selecting both options on any task selects every task instance in the run. On a large Dag (~1,300 tasks) this made the dialog propose clearing the entire run instead of one branch.
Selecting only Upstream, or only Downstream, returns the expected set.
What you think should happen instead?
Upstream + Downstream should return the union of the task's own ancestors and the task's own descendants (the Airflow 2 behaviour), not the descendants of its ancestors.
How to reproduce
from airflow.sdk import DAG
from airflow.providers.standard.operators.empty import EmptyOperator
with DAG("clear_repro", schedule=None):
root = EmptyOperator(task_id="root")
a = EmptyOperator(task_id="a")
b = EmptyOperator(task_id="b")
other = EmptyOperator(task_id="other")
root >> a >> b
root >> other
Trigger a run (run1), then dry-run a clear of task a:
POST /api/v2/dags/clear_repro/clearTaskInstances
{"dag_run_id": "run1", "task_ids": ["a"], "include_upstream": ..., "include_downstream": ...,
"only_failed": false, "dry_run": true}
Result on 3.3.1:
| include_upstream |
include_downstream |
returned task_ids |
expected |
| true |
false |
a, root |
a, root |
| false |
true |
a, b |
a, b |
| true |
true |
a, b, other, root |
a, b, root |
other is neither upstream nor downstream of a.
Operating System
Debian (official apache/airflow:slim-3.3.1-python3.12 image); reproduced locally on macOS with SQLite.
Versions of Apache Airflow Providers
apache-airflow-providers-standard 1.18.0 (not relevant to the bug)
Deployment
Other Docker-based deployment (Kubernetes, KubernetesExecutor)
Deployment details
No response
Anything else?
It looks like this comes from the dag_run_id branch of post_clear_task_instances (airflow/api_fastapi/core_api/routes/public/task_instances.py):
if upstream:
_collect_relatives(dag_run_id, "upstream")
if downstream:
_collect_relatives(dag_run_id, "downstream")
Both calls update normal_task_ids in place, so the downstream pass starts from the set that already contains the upstream tasks. Before #57758 (3.1.4), a single dag.partial_subset(include_upstream=..., include_downstream=...) computed both directions from the original task. The code on main looks the same.
A possible fix: run both directions from a copy of the original seed set and union the results, plus a test that enables both flags together.
Workaround: clear twice, once with Upstream only and once with Downstream only.
Are you willing to submit PR?
Code of Conduct
Apache Airflow version
3.3.1
What happened?
When clearing a task instance for a specific Dag run with both
include_upstreamandinclude_downstreamenabled (the "Upstream" + "Downstream" toggles in the Clear Task Instance dialog, orPOST /api/v2/dags/{dag_id}/clearTaskInstanceswith adag_run_id), Airflow also selects tasks that are neither upstream nor downstream of the selected task.The downstream traversal starts from the selected task plus all of its upstream tasks, so it picks up every descendant of those ancestors. In a Dag where all branches hang off a shared root task, selecting both options on any task selects every task instance in the run. On a large Dag (~1,300 tasks) this made the dialog propose clearing the entire run instead of one branch.
Selecting only Upstream, or only Downstream, returns the expected set.
What you think should happen instead?
Upstream + Downstream should return the union of the task's own ancestors and the task's own descendants (the Airflow 2 behaviour), not the descendants of its ancestors.
How to reproduce
Trigger a run (
run1), then dry-run a clear of taska:Result on 3.3.1:
otheris neither upstream nor downstream ofa.Operating System
Debian (official
apache/airflow:slim-3.3.1-python3.12image); reproduced locally on macOS with SQLite.Versions of Apache Airflow Providers
apache-airflow-providers-standard 1.18.0 (not relevant to the bug)
Deployment
Other Docker-based deployment (Kubernetes, KubernetesExecutor)
Deployment details
No response
Anything else?
It looks like this comes from the
dag_run_idbranch ofpost_clear_task_instances(airflow/api_fastapi/core_api/routes/public/task_instances.py):Both calls update
normal_task_idsin place, so the downstream pass starts from the set that already contains the upstream tasks. Before #57758 (3.1.4), a singledag.partial_subset(include_upstream=..., include_downstream=...)computed both directions from the original task. The code onmainlooks the same.A possible fix: run both directions from a copy of the original seed set and union the results, plus a test that enables both flags together.
Workaround: clear twice, once with Upstream only and once with Downstream only.
Are you willing to submit PR?
Code of Conduct