Uh oh!
There was an error while loading. Please reload this page.
Fire failure listeners when TriggerDagRunOperator API call fails - #70506
Fire failure listeners when TriggerDagRunOperator API call fails#70506ABiscuitttt wants to merge 3 commits into
Conversation
Some context on how we ran into this: we run a global failure-alert listener plugin that pages our on-call channel (Feishu/Lark) through the @hookimpldefon_task_instance_failed(previous_state, task_instance, error):
iftask_instance.state!=TaskInstanceState.FAILED:
return# still up_for_retry; only alert on final failuresend_alert(task_instance, error) # internal notificationWhen a |
102c4be to
9eef99eCompareWhen TriggerDagRunOperator targets a nonexistent DAG (or the API server otherwise rejects the trigger with 403/5xx), the supervisor reports the error by raising AirflowRuntimeError from the TriggerDagRun comms round-trip. That raise happens inside run()'s `except DagRunTriggerException` handler, and an exception raised in an except handler is not caught by the sibling except clauses, so it escaped run() entirely. main() then never reached finalize() — the only place on_task_instance_failed and on_failure_callback listeners run — so the task showed FAILED in the UI (via the supervisor's exit-code fallback) while alerting plugins and failure callbacks were silently skipped. Route the error through the normal failure path instead, so the task returns a terminal state, finalize() fires the listeners, and retry policy is honored. closes: #63089
Airflow prose convention is "Dag", and the test module already imports TriggerDagRunOperator at the top level.
9eef99e to
f5b3bceCompareABiscuitttt
commented
Aug 7, 2026
Closing as superseded by #70719, which addresses the same |
When
TriggerDagRunOperatortargets a nonexistent Dag (or the API server otherwise rejects the trigger with 403/5xx), the supervisor reports the error by raisingAirflowRuntimeErrorfrom theTriggerDagRuncomms round-trip. That raise happens insiderun()'sexcept DagRunTriggerExceptionhandler, and an exception raised in an except handler is not caught by the sibling except clauses — it escapedrun()entirely.main()then never reachedfinalize(), the only placeon_task_instance_failed/on_failure_callbacklisteners run. The task still showed FAILED in the UI (via the supervisor's exit-code fallback), but alerting plugins were silently skipped for exactly this failure mode.The fix routes the error through the normal failure path (
_handle_current_task_failed), so the task returns a terminal state,finalize()fires the listeners, and retry policy is honored (UP_FOR_RETRYwhen retries remain).Relationship to prior work on #63089: the reported
UnboundLocalErrorsymptom was already fixed by #67955 (thestatevariable is now initialized and theti.finishstats emission is guarded), and #63207 was an earlier, unmerged attempt at the full fix. What remains — and what this PR addresses — is the API error escapingrun()entirely, which silently skips failure listeners, failure callbacks, and retry handling.Regression tests cover both the no-retry (
FAILED) and retry (UP_FOR_RETRY) paths, asserting the terminal state reaches the supervisor instead of the error escapingrun().closes: #63089
Was generative AI tooling used to co-author this PR?
Generated-by: Kimi Code CLI following the guidelines