This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-3-test by this push:
new 1e0ca1eb6d7 [v3-3-test] Fix 500 when combining last-run and any-run
Dag state filters (#71366) (#71371)
1e0ca1eb6d7 is described below
commit 1e0ca1eb6d7180879d28833db35f8499a81467e5
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Aug 10 20:11:10 2026 +0530
[v3-3-test] Fix 500 when combining last-run and any-run Dag state filters
(#71366) (#71371)
(cherry picked from commit 7cdb9ad47aff1168a6de06363066184dd029d8b9)
Co-authored-by: Rahul Vats <[email protected]>
---
.../src/airflow/api_fastapi/common/parameters.py | 10 +++++++---
.../unit/api_fastapi/core_api/routes/ui/test_dags.py | 18 ++++++++++++++++++
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py
b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index 9f53cfaedbb..db873ad9c11 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -38,6 +38,7 @@ from pydantic import AfterValidator, BaseModel, NonNegativeInt
from sqlalchemy import Column, String, and_, func, not_, or_, select as
sql_select, true as sql_true
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.inspection import inspect
+from sqlalchemy.orm import aliased
from sqlalchemy.sql.functions import FunctionElement
from airflow._shared.timezones import timezone
@@ -1279,10 +1280,13 @@ class _AnyDagRunStateFilter(BaseParam[DagRunState |
None]):
if self.value is None and self.skip_none:
return select
- # EXISTS resolves each Dag via the (dag_id, state) index instead of
scanning every run in the state.
+ # Alias DagRun so this EXISTS subquery cannot auto-correlate to a
DagRun the outer query
+ # may already reference (e.g. the last_dag_run_state filter), which
would strip the
+ # subquery's FROM and raise. EXISTS resolves each Dag via the (dag_id,
state) index.
+ any_run = aliased(DagRun)
has_run_in_state = (
- sql_select(DagRun.dag_id)
- .where(DagRun.dag_id == DagModel.dag_id, DagRun.state ==
self.value)
+ sql_select(any_run.dag_id)
+ .where(any_run.dag_id == DagModel.dag_id, any_run.state ==
self.value)
.exists()
)
return select.where(has_run_in_state)
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
index 04fa6876d47..d37e0866ae9 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
@@ -160,6 +160,24 @@ class TestGetDagRuns(TestPublicDagEndpoint):
assert any_state.status_code == 200
assert [dag["dag_id"] for dag in any_state.json()["dags"]] == [DAG1_ID]
+ @pytest.mark.usefixtures("configure_git_connection_for_dag_bundle")
+ def test_last_and_any_run_state_filters_combined(self, test_client,
session):
+ # Regression: combining the last-run and any-run state filters must
return the
+ # intersection, not raise. The any-run EXISTS subquery must not
correlate to the
+ # DagRun the last-run filter joins into the outer query.
+ latest_run = session.scalar(
+ select(DagRun).where(DagRun.dag_id == DAG1_ID, DagRun.run_id ==
"run_id_5")
+ )
+ latest_run.state = DagRunState.FAILED
+ session.commit()
+
+ response = test_client.get(
+ "/dags",
+ params={"last_dag_run_state": "failed", "dag_run_state": "failed",
"dag_ids": [DAG1_ID]},
+ )
+ assert response.status_code == 200
+ assert [dag["dag_id"] for dag in response.json()["dags"]] == [DAG1_ID]
+
@pytest.fixture
def setup_hitl_data(self, create_task_instance: TaskInstance, session:
Session):
"""Setup HITL test data for parametrized tests."""