-
Notifications
You must be signed in to change notification settings - Fork 16.4k
Open
Labels
affected_version:2.11Issues Reported for 2.10Issues Reported for 2.10area:Schedulerincluding HA (high availability) schedulerincluding HA (high availability) schedulerarea:corekind:bugThis is a clearly a bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yetlabel for new issues that we didn't triage yet
Description
Apache Airflow version
2.11.0
If "Other Airflow 2 version" selected, which one?
No response
What happened?
- A DAG defines a custom operator that accepts
start_date/end_datestrings and passes them into anargslist. The task instance is created with a Jinja-templated string forend_date(and/orstart_date). - The operator does not list these fields in
template_fields. - Airflow serializes the DAG and stores the templated string (e.g.,
"{{ (data_interval_end - macros.timedelta(days=4)).strftime('%Y-%m-%d') }}") under theend_datekey. - When the scheduler later creates a DAG run and needs to deserialize, it treats any key ending with
_dateas a datetime and force-coerces it via_deserialize_datetime. Because the value is a string template, deserialization raises aTypeErrorand the scheduler crashes.
What you think should happen instead?
- The scheduler should never crash due to a single misconfigured/bad DAG. It should log and skip the DAG.
- Deserialization should not attempt to coerce templated strings or non-timestamp values for fields matching
*_date. - Proposed remediations (happy to submit a PR):
- Scheduler guard: wrap
dag = self.dagbag.get_dag(dag_model.dag_id, session=session)intry/exceptin both_create_dag_runsand_create_dag_runs_dataset_triggered, similar to the existing guard arounddag.create_dagrun(...). - Deserialization guard: in
airflow/serialization/serialized_objects.py, change the_datehandling to be safe for non-numeric values, e.g.:- Only coerce when
not isinstance(v, str), or - Wrap
_deserialize_datetime(v)intry/exceptand leavevas-is on failure.
Both can be complementary. The scheduler guard provides resilience. The deserialization guard fixes the root cause across components (scheduler/webserver/CLI).
- Only coerce when
- Scheduler guard: wrap
How to reproduce
- Define operators:
from airflow.models.baseoperator import BaseOperator
from airflow.utils.decorators import apply_defaults
class FooOperator(BaseOperator):
# Trigger of the bug: template_fields = ("args",) is a tuple but ("args") is a string
template_fields = ("args")
@apply_defaults
def __init__(self, args=None, **kwargs):
super().__init__(**kwargs)
self.args = args
def execute(self, context):
self.log.info("Executing FooOperator with args: %s", self.args)
class BarOperator(FooOperator):
def __init__(self, start_date: str, end_date: str, **kwargs):
super().__init__(
args=["--start_date", start_date, "--end_date", end_date],
**kwargs,
)- Define DAG:
from airflow.models import DAG
from airflow.operators.empty import EmptyOperator
START_DATE = "{{ (data_interval_start - macros.timedelta(days=11)).strftime('%Y-%m-%d') }}"
END_DATE = "{{ (data_interval_end - macros.timedelta(days=4)).strftime('%Y-%m-%d') }}"
with DAG(
dag_id="foo_bar_dag",
schedule_interval="0 7 * * *",
catchup=False,
max_active_runs=1,
) as dag:
start = EmptyOperator(task_id="start")
end = EmptyOperator(task_id="end")
foo_task = BarOperator(
task_id="foo_task",
start_date=START_DATE,
end_date=END_DATE,
)
start >> foo_task >> end- Behavior:
- On initial parse, the serialized DAG stores
end_dateas the templated string. - During DAG run creation, the scheduler deserializes and attempts to coerce
end_datevia_deserialize_datetime, which expects a numeric timestamp, causingTypeErrorand crashing the scheduler.
Operating System
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian
Versions of Apache Airflow Providers
Not applicable
Deployment
Docker-Compose
Deployment details
No response
Anything else?
-
Relation to Issue DAG fails serialization if template_field contains execution_timeout #29819 / PR Add a check for not templateable fields #29821 (forbidden_fields):
- PR Add a check for not templateable fields #29821 introduced a
forbidden_fieldscheck to prevent listing BaseOperator constructor args (e.g.,execution_timeout,start_date,end_date) intemplate_fields, preventing one class of crash. - This issue is the inverse: a templated string in a
*_datefield that is not templated. The deserializer coerces any*_datevalue, which fails on strings.forbidden_fieldsdoes not intercept this case and also forbids addingstart_date/end_datetotemplate_fieldsto skip coercion.
- PR Add a check for not templateable fields #29821 introduced a
-
Proposal:
- Scheduler: add try/except around
get_dag(...)in_create_dag_runsand_create_dag_runs_dataset_triggered. - Deserialization: guard
_datecoercion (type-check or try/except) in both operator and DAG deserialization paths.
- Scheduler: add try/except around
Are you willing to submit PR?
- Yes I am willing to submit a PR!
Code of Conduct
- I agree to follow this project's Code of Conduct
Metadata
Metadata
Assignees
Labels
affected_version:2.11Issues Reported for 2.10Issues Reported for 2.10area:Schedulerincluding HA (high availability) schedulerincluding HA (high availability) schedulerarea:corekind:bugThis is a clearly a bugThis is a clearly a bugneeds-triagelabel for new issues that we didn't triage yetlabel for new issues that we didn't triage yet