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 84cd209ffd2 [v3-3-test] Return 404 instead of 500 when ti_run cannot
find the DagRun (#72890) (#72900)
84cd209ffd2 is described below
commit 84cd209ffd28235cb2730c28d175292ce51e0b2c
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 11 14:25:06 2026 +0530
[v3-3-test] Return 404 instead of 500 when ti_run cannot find the DagRun
(#72890) (#72900)
* [v3-3-test] Return 404 instead of 500 when ti_run cannot find the DagRun
(#72890)
* Return 404 instead of 500 when ti_run cannot find the DagRun
A missing DagRun in the Execution API ti_run route raised a bare
ValueError, which escaped the route's DataError/SQLAlchemyError guards and
surfaced through the app-level catch-all as an opaque 500. A missing
resource should be reported to the caller as a 404, consistent with the
other not-found paths in this route.
* Update
airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
Co-authored-by: Henry Chen <[email protected]>
* Align test with the updated missing-DagRun error message
* Keep the ti_run 404 test's scalars mock out of fixture setup
Patching Session.scalars as a decorator also mocked it while
create_task_instance built the fixture, so the DAG bulk-write only worked
because the tables were cleared first. Scope the patch to the request
itself, matching the sibling database-error test.
---------
(cherry picked from commit a9f6c898f01dc1e5e1dc18172f5bc65097957a71)
Co-authored-by: Pierre Jeambrun <[email protected]>
Co-authored-by: Henry Chen <[email protected]>
* Define RUN_PAYLOAD on TestTIRunState so the ti_run 404 test can build its
request
---------
Co-authored-by: Pierre Jeambrun <[email protected]>
Co-authored-by: Henry Chen <[email protected]>
Co-authored-by: Rahul Vats <[email protected]>
Co-authored-by: Rahul Vats <[email protected]>
---
.../execution_api/routes/task_instances.py | 8 +++++-
.../versions/head/test_task_instances.py | 32 ++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
b/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
index 83d8dc7c51e..f8b8d7e7de1 100644
---
a/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
+++
b/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
@@ -265,7 +265,13 @@ def ti_run(
if not dr:
log.error("DagRun not found", dag_id=ti.dag_id, run_id=ti.run_id)
- raise ValueError(f"DagRun with dag_id={ti.dag_id} and
run_id={ti.run_id} not found.")
+ raise HTTPException(
+ status_code=status.HTTP_404_NOT_FOUND,
+ detail={
+ "reason": "not_found",
+ "message": f"DagRun with dag_id={ti.dag_id} and
run_id={ti.run_id} not found",
+ },
+ )
# Send the keys to the SDK so that the client requests to clear those
XComs from the server.
# The reason we cannot do this here in the server is because we need
to issue a purge on custom XCom backends
diff --git
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_task_instances.py
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_task_instances.py
index 65bbe0ff77c..7d4d5cc7433 100644
---
a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_task_instances.py
+++
b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_task_instances.py
@@ -163,6 +163,14 @@ def test_id_matches_sub_claim(client, session,
create_task_instance):
class TestTIRunState:
+ RUN_PAYLOAD = {
+ "state": "running",
+ "hostname": "random-hostname",
+ "unixname": "random-unixname",
+ "pid": 100,
+ "start_date": "2024-10-31T12:00:00Z",
+ }
+
def setup_method(self):
clear_db_logs()
clear_db_runs()
@@ -207,6 +215,30 @@ class TestTIRunState:
events = response.json()["dag_run"]["consumed_asset_events"]
assert [e["partition_key"] for e in events] == ["2024-01-15"]
+ def test_ti_run_missing_dagrun_returns_404(self, client, session,
create_task_instance):
+ """A missing DagRun must surface as a clean 404, not an internal
500."""
+ ti = create_task_instance(
+ task_id="test_ti_run_missing_dagrun",
+ state=State.QUEUED,
+ session=session,
+ )
+ session.commit()
+
+ # Patch only around the request so fixture setup above is untouched;
force the DagRun
+ # lookup (the only scalars() call before the guard) to return None.
+ with mock.patch("sqlalchemy.orm.Session.scalars", autospec=True) as
mock_scalars:
+
mock_scalars.return_value.unique.return_value.one_or_none.return_value = None
+ response = client.patch(
+ f"/execution/task-instances/{ti.id}/run",
+ json=self.RUN_PAYLOAD,
+ )
+
+ assert response.status_code == 404
+ assert response.json()["detail"] == {
+ "reason": "not_found",
+ "message": f"DagRun with dag_id={ti.dag_id} and run_id={ti.run_id}
not found",
+ }
+
@pytest.mark.parametrize(
("max_tries", "should_retry"),
[