luc-pimentel opened a new issue, #75254:
URL: https://github.com/apache/airflow/issues/75254
### Under which category would you file this issue?
Airflow Core
### Apache Airflow version
3.3.2; also reproduced on main on October 20, 2026.
### What happened and how to reproduce it?
When a Dag stops deserializing (for example a custom timetable whose
`deserialize` reads a Variable that was deleted) and one of its tasks is in
`scheduled` with `max_active_tis_per_dag` or `max_active_tis_per_dagrun` set,
the scheduler exits with an uncaught `DeserializationError` on its first loop
after a restart, and again on every restart. Every other Dag stops being
scheduled until that task is cleared or the Dag loads again. #74499 fixed the
same crash for a queued run of such a Dag; a running run with a scheduled task
still has it.
Plugin (`plugins/probe_timetable.py`): a timetable that fails to deserialize
once a flag file is gone, standing in for one that reads a missing Variable.
```python
import os
from airflow.plugins_manager import AirflowPlugin
from airflow.timetables.base import DataInterval, Timetable
class FlagTimetable(Timetable):
def infer_manual_data_interval(self, *, run_after):
return DataInterval(run_after, run_after)
def next_dagrun_info(self, *, last_automated_data_interval, restriction):
return None
def serialize(self):
return {}
@classmethod
def deserialize(cls, data):
if not os.path.exists(os.environ["PROBE_FLAG"]):
raise ValueError("flag missing, cannot deserialize
FlagTimetable")
return cls()
class ProbeTimetablePlugin(AirflowPlugin):
name = "probe_timetable"
timetables = [FlagTimetable]
```
Dag file:
```python
from airflow.providers.standard.operators.bash import BashOperator
from airflow.sdk import DAG
from probe_timetable import FlagTimetable
with DAG("probe_bad_long", schedule=FlagTimetable(), catchup=False):
BashOperator(task_id="t1", bash_command="sleep 600",
max_active_tis_per_dag=1)
```
With `PROBE_FLAG` pointing at an existing file and `airflow standalone`
running (LocalExecutor, SQLite, examples off, Dags unpaused at creation),
trigger two runs of `probe_bad_long` through the API. Run 1's `t1` starts; run
2's `t1` stays `scheduled` behind `max_active_tis_per_dag=1`. Then stop
everything, delete the flag file, and start the scheduler alone:
```sh
rm "$PROBE_FLAG"
airflow scheduler --num-runs 8; echo "exit $?"
```
Result on 3.3.2 and main: the scheduler logs "Error scheduling DAG run r2 of
probe_bad_long" (and r1) with the `ValueError`, and skips those runs as
expected, then exits with code 1 from `_task_concurrency_allows_execution`:
```
File ".../airflow/jobs/scheduler_job_runner.py", line 932, in
_select_task_instances_to_queue
self._task_concurrency_allows_execution(
File ".../airflow/jobs/scheduler_job_runner.py", line 615, in
_task_concurrency_allows_execution
serialized_dag = self.scheduler_dag_bag.get_dag_for_run(
...
airflow.exceptions.DeserializationError: An unexpected error occurred while
trying to deserialize Dag 'probe_bad_long'
```
Run 2's `t1` is still `scheduled`, so starting the scheduler again exits the
same way. With the flag file back, the same command finishes its loops and
queues `t1`. The same Dag without `max_active_tis_per_dag` only gets the "Error
scheduling DAG run" log and the scheduler keeps going, because the task
concurrency check is the only place on this path that loads the Dag again.
### What you think should happen instead?
Log the error and skip the task, as `_schedule_all_dag_runs` does for the
run and as #74499 made `_start_queued_dagruns` do for queued runs, so one Dag
that no longer loads does not stop every other Dag from being scheduled.
In `airflow-core/src/airflow/jobs/scheduler_job_runner.py`,
`_task_concurrency_allows_execution` calls `scheduler_dag_bag.get_dag_for_run`
without catching the deserialization error; it is called from
`_select_task_instances_to_queue` for every scheduled task whose Dag has a task
concurrency limit, and the error propagates out of the scheduler loop.
### Operating System
macOS 26.2, Python 3.12 for 3.3.2; Linux/Breeze, Python 3.11 for main.
### Deployment
Virtualenv installation
### Versions of Apache Airflow Providers
`apache-airflow-providers-standard==1.19.0`
### Anything else?
No duplicate found in open/closed issue and PR searches for "scheduler crash
deserialize", "DeserializationError scheduler", "max_active_tis_per_dag crash",
"timetable deserialize Variable" and `_task_concurrency_allows_execution`.
#62050/#72243 are about the same function when the serialized Dag row is
missing (it returns `None`), not when deserialization raises. #54771 is a
different deserialization failure.
### Are you willing to submit PR?
No. I am reporting the bug and am not planning to submit a PR.
### Code of Conduct
- [x] I agree to follow this project's [Code of
Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md).
---
Drafted-by: Claude Code 2.1.304 (Claude Fable 5.1); reviewed by
@luc-pimentel before posting
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]