From 9887f371f3b40d55ca6575188ec410ba36446397 Mon Sep 17 00:00:00 2001 From: david-parkk Date: Wed, 13 May 2026 21:07:44 +0900 Subject: [PATCH 1/3] fix: raise InvalidBackfillDate if from_date is after to_date --- airflow-core/src/airflow/models/backfill.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/models/backfill.py b/airflow-core/src/airflow/models/backfill.py index 03acbb1f13374..edb99393b46c0 100644 --- a/airflow-core/src/airflow/models/backfill.py +++ b/airflow-core/src/airflow/models/backfill.py @@ -266,6 +266,16 @@ def _validate_backfill_params( reprocess_behavior: ReprocessBehavior | None, dag_run_conf: dict | None = None, ) -> None: + + if from_date > to_date: + raise InvalidBackfillDate( + f"from_date ({from_date.isoformat()}) must not be after to_date ({to_date.isoformat()})." + ) + + current_time = timezone.utcnow() + if from_date >= current_time and to_date >= current_time: + raise InvalidBackfillDate("Backfill cannot be executed for future dates.") + depends_on_past = any(x.depends_on_past for x in dag.tasks) if depends_on_past: if reverse is True: @@ -277,9 +287,6 @@ def _validate_backfill_params( "Dag has tasks for which depends_on_past=True. " "You must set reprocess behavior to reprocess completed or reprocess failed." ) - current_time = timezone.utcnow() - if from_date >= current_time and to_date >= current_time: - raise InvalidBackfillDate("Backfill cannot be executed for future dates.") if dag_run_conf is not None: try: dag.params.deep_merge(dag_run_conf).validate() From a878581ef5e39b1d3175e2aedeaab99c7ec5df33 Mon Sep 17 00:00:00 2001 From: david-parkk Date: Wed, 13 May 2026 21:08:45 +0900 Subject: [PATCH 2/3] test: valid InvalidBackfillDate Exception when backfill parameter from_date is after to_date --- airflow-core/tests/unit/models/test_backfill.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/airflow-core/tests/unit/models/test_backfill.py b/airflow-core/tests/unit/models/test_backfill.py index 25267b4a5c724..1fed8f4d45071 100644 --- a/airflow-core/tests/unit/models/test_backfill.py +++ b/airflow-core/tests/unit/models/test_backfill.py @@ -34,6 +34,7 @@ BackfillDagRunExceptionReason, DagNonPeriodicScheduleException, InvalidBackfillConf, + InvalidBackfillDate, InvalidBackfillDirection, InvalidReprocessBehavior, ReprocessBehavior, @@ -814,3 +815,19 @@ def test_do_dry_run_non_periodic_schedule_rejected(schedule, dag_kwargs, dag_mak session=session, ) ) + +def test_create_backfill_from_date_after_to_date_raises(dag_maker, session): + with dag_maker(schedule="@daily") as dag: + PythonOperator(task_id="hi", python_callable=print) + session.commit() + + with pytest.raises(InvalidBackfillDate, match="must not be after to_date"): + _create_backfill( + dag_id=dag.dag_id, + from_date=pendulum.parse("2026-05-13"), + to_date=pendulum.parse("2026-05-12"), + max_active_runs=2, + reverse=False, + triggering_user_name="pytest", + dag_run_conf={}, + ) From 5f7d18e9ab1c87fcfff66f1cf4b43df140b95203 Mon Sep 17 00:00:00 2001 From: david-parkk Date: Wed, 13 May 2026 21:26:34 +0900 Subject: [PATCH 3/3] feat: add exception InvalidBackfillDateRange - ambigous meaning of InvalidBackfillDate --- .../api_fastapi/core_api/routes/public/backfills.py | 3 +++ airflow-core/src/airflow/models/backfill.py | 10 +++++++++- airflow-core/tests/unit/models/test_backfill.py | 5 +++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/backfills.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/backfills.py index e9c28d4fba9fd..201be5c268984 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/backfills.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/backfills.py @@ -52,6 +52,7 @@ DagNonPeriodicScheduleException, InvalidBackfillConf, InvalidBackfillDate, + InvalidBackfillDateRange, InvalidBackfillDirection, InvalidReprocessBehavior, _create_backfill, @@ -265,6 +266,7 @@ def create_backfill( InvalidBackfillDirection, DagNonPeriodicScheduleException, InvalidBackfillDate, + InvalidBackfillDateRange, InvalidBackfillConf, ) as e: raise RequestValidationError(str(e)) @@ -318,6 +320,7 @@ def create_backfill_dry_run( InvalidBackfillDirection, DagNonPeriodicScheduleException, InvalidBackfillDate, + InvalidBackfillDateRange, InvalidBackfillConf, ) as e: raise RequestValidationError(str(e)) diff --git a/airflow-core/src/airflow/models/backfill.py b/airflow-core/src/airflow/models/backfill.py index edb99393b46c0..a5ae0e0739cc9 100644 --- a/airflow-core/src/airflow/models/backfill.py +++ b/airflow-core/src/airflow/models/backfill.py @@ -107,6 +107,14 @@ class InvalidBackfillDate(AirflowException): """ +class InvalidBackfillDateRange(AirflowException): + """ + Raised when from_date is after to_date in a backfill request. + + :meta private: + """ + + class InvalidBackfillConf(AirflowException): """ Raised when the provided ``dag_run_conf`` fails validation against the DAG's params. @@ -268,7 +276,7 @@ def _validate_backfill_params( ) -> None: if from_date > to_date: - raise InvalidBackfillDate( + raise InvalidBackfillDateRange( f"from_date ({from_date.isoformat()}) must not be after to_date ({to_date.isoformat()})." ) diff --git a/airflow-core/tests/unit/models/test_backfill.py b/airflow-core/tests/unit/models/test_backfill.py index 1fed8f4d45071..c4c787408c39c 100644 --- a/airflow-core/tests/unit/models/test_backfill.py +++ b/airflow-core/tests/unit/models/test_backfill.py @@ -34,7 +34,7 @@ BackfillDagRunExceptionReason, DagNonPeriodicScheduleException, InvalidBackfillConf, - InvalidBackfillDate, + InvalidBackfillDateRange, InvalidBackfillDirection, InvalidReprocessBehavior, ReprocessBehavior, @@ -816,12 +816,13 @@ def test_do_dry_run_non_periodic_schedule_rejected(schedule, dag_kwargs, dag_mak ) ) + def test_create_backfill_from_date_after_to_date_raises(dag_maker, session): with dag_maker(schedule="@daily") as dag: PythonOperator(task_id="hi", python_callable=print) session.commit() - with pytest.raises(InvalidBackfillDate, match="must not be after to_date"): + with pytest.raises(InvalidBackfillDateRange, match="must not be after to_date"): _create_backfill( dag_id=dag.dag_id, from_date=pendulum.parse("2026-05-13"),