This is an automated email from the ASF dual-hosted git repository.
ephraimanierobi pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 7b226f1dd9b9209de39e55b8a5df67d7f81f12c1
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jan 14 10:17:15 2026 +0100
[v3-1-test] Unique run_id across manually triggered Dags with schedules
(#59477) (#60468)
* run id no longer collides in manual runs
* ruff format;
(cherry picked from commit 13f3e267fb85970de7b25bf41019fcfade8ba014)
Co-authored-by: Steve Ahn <[email protected]>
---
.../api_fastapi/core_api/datamodels/dag_run.py | 5 +---
.../core_api/routes/public/test_dag_run.py | 35 ++++++++++++++++++++++
2 files changed, 36 insertions(+), 4 deletions(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py
index 96c3a3a13ad..1160842dd2c 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dag_run.py
@@ -123,10 +123,7 @@ class TriggerDAGRunPostBody(StrictBaseModel):
end=timezone.coerce_datetime(self.data_interval_end),
)
else:
- data_interval = dag.timetable.infer_manual_data_interval(
- run_after=coerced_logical_date or
timezone.coerce_datetime(run_after)
- )
- run_after = data_interval.end
+ data_interval =
dag.timetable.infer_manual_data_interval(run_after=coerced_logical_date)
run_id = self.dag_run_id or dag.timetable.generate_run_id(
run_type=DagRunType.MANUAL,
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
index 47baca3046f..63088ba4f39 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
@@ -1836,6 +1836,41 @@ class TestTriggerDagRun:
"note": None,
}
+ @pytest.mark.usefixtures("configure_git_connection_for_dag_bundle")
+ def test_should_generate_unique_run_id_for_scheduled_dag(self, dag_maker,
test_client, session):
+ "Ensure manual triggers on scheduled DAGs don't conflict on run_id"
+ scheduled_dag_id = "test_scheduled_dag"
+ with dag_maker(
+ dag_id=scheduled_dag_id,
+ schedule="@daily",
+ start_date=START_DATE1,
+ session=session,
+ serialized=True,
+ ):
+ EmptyOperator(task_id="test_task")
+
+ session.commit()
+
+ response_1 = test_client.post(
+ f"/dags/{scheduled_dag_id}/dagRuns",
+ json={
+ "logical_date": "2025-12-11T16:00:00+00:00",
+ "run_after": "2025-12-11T16:00:00+00:00",
+ },
+ )
+ assert response_1.status_code == 200
+
+ response_2 = test_client.post(
+ f"/dags/{scheduled_dag_id}/dagRuns",
+ json={
+ "logical_date": "2025-12-11T16:01:00+00:00",
+ "run_after": "2025-12-11T16:01:00+00:00",
+ },
+ )
+ assert response_2.status_code == 200
+
+ assert response_1.json()["dag_run_id"] !=
response_2.json()["dag_run_id"]
+
@time_machine.travel("2025-10-02 12:00:00", tick=False)
@pytest.mark.usefixtures("custom_timetable_plugin")
def test_custom_timetable_generate_run_id_for_manual_trigger(self,
dag_maker, test_client, session):