Make airflow dags test wait for Human-in-the-loop input instead of hanging#68492
Merged
Merged
Conversation
b26e9a6 to
c7a56c2
Compare
phanikumv
reviewed
Jun 13, 2026
shahar1
reviewed
Jun 13, 2026
shahar1
left a comment
Contributor
There was a problem hiding this comment.
Please add an AI usage statement :)
Member
Author
Dropped an email reply on the dev list around it, thanks. |
Dev-iL
reviewed
Jun 14, 2026
Dev-iL
left a comment
Collaborator
There was a problem hiding this comment.
Do we want to have cli flags like --hitl-auto-approve and --hitl-auto-reject so users can test hitl dags without actually interacting with them?
958db37 to
5d936ed
Compare
…nging Previously a HITL task that parked in awaiting_input made `airflow dags test` loop "No tasks to run. unrunnable tasks: ..." once per second forever, with no way to make progress (the in-process runner also swallows SIGTERM, so even `timeout` could not stop it). dag.test() now treats parked HITL tasks as waiting rather than unrunnable, and never resolves them itself: the run stays alive, logging which tasks await input, until a response recorded from outside flips them back to SCHEDULED -- at which point the existing loop resumes them. This matches how a parked task behaves on a real deployment, and the existing response channels work unchanged: the Required Actions UI or the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the metadata database (e.g. airflow standalone). Humans and AI agents can drive HITL pipelines locally by running dags test and submitting the response through that API. Interactive console prompting in the dags test CLI is left for a follow-up.
5d936ed to
6cccc82
Compare
vatsrahul1001
approved these changes
Jun 28, 2026
Contributor
Backport successfully created: v3-3-testNote: As of Merging PRs targeted for Airflow 3.X In matter of doubt please ask in #release-management Slack channel.
|
shahar1
pushed a commit
that referenced
this pull request
Jun 28, 2026
…nstead of hanging (#68492) (#69104) * Make airflow dags test wait for Human-in-the-loop input instead of hanging Previously a HITL task that parked in awaiting_input made `airflow dags test` loop "No tasks to run. unrunnable tasks: ..." once per second forever, with no way to make progress (the in-process runner also swallows SIGTERM, so even `timeout` could not stop it). dag.test() now treats parked HITL tasks as waiting rather than unrunnable, and never resolves them itself: the run stays alive, logging which tasks await input, until a response recorded from outside flips them back to SCHEDULED -- at which point the existing loop resumes them. This matches how a parked task behaves on a real deployment, and the existing response channels work unchanged: the Required Actions UI or the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the metadata database (e.g. airflow standalone). Humans and AI agents can drive HITL pipelines locally by running dags test and submitting the response through that API. Interactive console prompting in the dags test CLI is left for a follow-up. * Document the HITL REST API calls for responding during dags test (cherry picked from commit 63cee22) Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com> Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
karenbraganz
pushed a commit
to karenbraganz/airflow
that referenced
this pull request
Jun 30, 2026
…nging (apache#68492) * Make airflow dags test wait for Human-in-the-loop input instead of hanging Previously a HITL task that parked in awaiting_input made `airflow dags test` loop "No tasks to run. unrunnable tasks: ..." once per second forever, with no way to make progress (the in-process runner also swallows SIGTERM, so even `timeout` could not stop it). dag.test() now treats parked HITL tasks as waiting rather than unrunnable, and never resolves them itself: the run stays alive, logging which tasks await input, until a response recorded from outside flips them back to SCHEDULED -- at which point the existing loop resumes them. This matches how a parked task behaves on a real deployment, and the existing response channels work unchanged: the Required Actions UI or the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the metadata database (e.g. airflow standalone). Humans and AI agents can drive HITL pipelines locally by running dags test and submitting the response through that API. Interactive console prompting in the dags test CLI is left for a follow-up. * Document the HITL REST API calls for responding during dags test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A task that reaches the
awaiting_inputstate (Human-in-the-loop, #68028) hangsairflow dags testforever: nothing can resume it, so the command logsNo tasks to run. unrunnable tasks: ...once per second indefinitely. The in-process task runner also swallows SIGTERM, sotimeoutcannot stop it.dag.test()now treats parked HITL tasks as waiting rather than unrunnable, and never resolves them itself: the run stays alive, logging which tasks await input, until a response recorded from outside flips them back to SCHEDULED -- the existing loop then resumes them. This is the same contract a parked task has on a real deployment, where the API response handler or the scheduler's timeout sweep performs the resume.Because the resume is the standard transition, the existing response channels work with
dags testunchanged: the Required Actions UI or the HITL REST API of an api-server sharing the metadata database (e.g.airflow standalone). Verified end to end in breeze:airflow dags testparks and logsWaiting for Human-in-the-loop input for tasks: ['wait_approval'], aPATCH .../hitlDetailswith{"chosen_options": ["Approve"]}against a locally startedairflow api-serverresumes it, the downstream task runs, and the run finishes successfully. This also gives AI agents a clean way to drive HITL pipelines locally: rundags test, surface the request to a human, submit the answer through the API.The change is deliberately minimal -- one hunk in the
dag.test()loop plus a contract test. Interactive console prompting in thedags testCLI (and any API additions it needs) is left for follow-up PRs.Was generative AI tooling used to co-author this PR?