You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by mo...@apache.org on 2026/04/27 15:40:24 UTC
(airflow) branch v3-2-test updated: [v3-2-test] run finalize when supervisor call fails (#65714) (#65946)
This is an automated email from the ASF dual-hosted git repository.
mobuchowski pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new caf94947b38 [v3-2-test] run finalize when supervisor call fails (#65714) (#65946)
caf94947b38 is described below
commit caf94947b382e5f20f0fece18b3a85294c028dd9
Author: github-actions[bot] <41...@users.noreply.github.com>
AuthorDate: Mon Apr 27 17:40:17 2026 +0200
[v3-2-test] run finalize when supervisor call fails (#65714) (#65946)
(cherry picked from commit 428f1337f1ff2b8d1fd3b23f0295ea3f18ea2019)
Signed-off-by: Maciej Obuchowski <ma...@datadoghq.com>
Co-authored-by: Maciej Obuchowski <ob...@gmail.com>
---
.../src/airflow/sdk/execution_time/task_runner.py | 12 +++++++-
.../task_sdk/execution_time/test_task_runner.py | 36 ++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/task-sdk/src/airflow/sdk/execution_time/task_runner.py b/task-sdk/src/airflow/sdk/execution_time/task_runner.py
index 1c251e66683..5a2624def02 100644
--- a/task-sdk/src/airflow/sdk/execution_time/task_runner.py
+++ b/task-sdk/src/airflow/sdk/execution_time/task_runner.py
@@ -1366,7 +1366,17 @@ def run(
Stats.incr("ti.finish", tags={**stats_tags, "state": state.value})
if msg:
- SUPERVISOR_COMMS.send(msg=msg)
+ # If the supervisor rejects the terminal-state report
+ # (e.g. the server already moved the TI to a terminal state and
+ # returns 409 - for example due to issue fixed by #65594),
+ # we still want to run finalize() so listeners observe the task state.
+ try:
+ SUPERVISOR_COMMS.send(msg=msg)
+ except Exception:
+ log.exception(
+ "Failed to report terminal task state to supervisor",
+ state=state.value,
+ )
# Return the message to make unit tests easier too
ti.state = state
diff --git a/task-sdk/tests/task_sdk/execution_time/test_task_runner.py b/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
index 88424bfe53e..9ceedc8ecaf 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_task_runner.py
@@ -421,6 +421,42 @@ def test_main_sends_reschedule_task_when_startup_reschedules(
]
+def test_run_swallows_supervisor_terminal_send_failure(create_runtime_ti, mock_supervisor_comms):
+ """
+ When the supervisor rejects the terminal-state send (e.g. the server
+ already moved the TI to a terminal state and returns 409), `run()` must
+ not let that exception escape — the task body succeeded, so `run()` should
+ still return (SUCCESS, msg, None) so `main()` can notify listeners with
+ the correct local terminal state.
+ """
+ from airflow.sdk.execution_time.task_runner import run
+
+ class TrivialOperator(BaseOperator):
+ def execute(self, context):
+ return "ok"
+
+ task = TrivialOperator(task_id="trivial")
+ runtime_ti = create_runtime_ti(task=task)
+ context = runtime_ti.get_template_context()
+ log = mock.MagicMock()
+
+ # Let every send succeed except the terminal-state send — simulate the
+ # server rejecting the SucceedTask with a 409-wrapped AirflowRuntimeError.
+ def send_side_effect(msg=None, **kwargs):
+ if isinstance(msg, SucceedTask):
+ raise RuntimeError("409 invalid_state: previous_state=success")
+ return mock.DEFAULT
+
+ mock_supervisor_comms.send.side_effect = send_side_effect
+
+ # Must not raise.
+ state, msg, error = run(runtime_ti, context, log)
+
+ assert state == TaskInstanceState.SUCCESS
+ assert isinstance(msg, SucceedTask)
+ assert error is None
+
+
def test_task_span_is_child_of_dag_run_span(make_ti_context):
"""Full trace hierarchy: dag_run → task_run.my_task (API server) → worker.my_task (task runner)."""
# Single provider shared by all spans so contexts are compatible.