airflow.providers.standard.operators.hitl

Classes

HITLOperator

Base class for all Human-in-the-loop Operators to inherit from.

ApprovalOperator

Human-in-the-loop Operator that has only 'Approval' and 'Reject' options.

HITLBranchOperator

BranchOperator based on Human-in-the-loop Response.

HITLEntryOperator

Human-in-the-loop Operator that is used to accept user input through TriggerForm.

Functions

request_input(*, subject, options[, body, defaults, ...])

Ask a person for input from inside a running Python task, and return their answer.

Module Contents

airflow.providers.standard.operators.hitl.request_input(*, subject, options, body=None, defaults=None, multiple=False, params=None, notifiers=None, assigned_users=None, timeout=None, key=None)[source]

Ask a person for input from inside a running Python task, and return their answer.

The first time a question is asked, the request appears on the Required Actions page, the same as one from HITLOperator, and the task stops there: it waits in the awaiting_input state without holding a worker slot. Once the request is answered, the task runs again from the start of its callable, and this call returns the answer instead of asking again. Save anything the second run should not redo (for example an agent framework’s run state) to task_state_store before calling this.

It works in @task and PythonOperator callables (sync or async), including @task.branch and @task.short_circuit. It can’t pause tasks that run their callable in another process, such as @task.virtualenv, @task.external_python, @task.docker or @task.kubernetes. Ask one question at a time from the task’s own code, not from worker threads or from several async calls running at once: a task instance has a single Required Actions request.

Needs Airflow 3.4+. Earlier versions keep the first request’s subject, body and options when a task asks again, after a resume or on a retry, so the reviewer would answer a question they were not shown.

Parameters:
  • subject (str) – Headline shown to the reviewer.

  • options (list[str]) – Options the reviewer chooses from.

  • body (str | None) – Details the reviewer needs to decide (Markdown).

  • defaults (str | list[str] | None) – Options applied if timeout passes without an answer. Without defaults, the task fails with HITLTimeoutError at the timeout.

  • multiple (bool) – Whether the reviewer can choose more than one option.

  • params (airflow.sdk.definitions.param.ParamsDict | dict[str, Any] | None) – Form fields for the reviewer to fill in, defined like Dag params.

  • notifiers (collections.abc.Sequence[airflow.sdk.bases.notifier.BaseNotifier] | airflow.sdk.bases.notifier.BaseNotifier | None) – Notifiers called with the task context once the request is written.

  • assigned_users (airflow.sdk.execution_time.hitl.HITLUser | list[airflow.sdk.execution_time.hitl.HITLUser] | None) – Users allowed to answer. Anyone with access can answer if not set.

  • timeout (datetime.timedelta | None) – How long to wait for an answer. Waits indefinitely if not set.

  • key (str | None) – Which question this is, so a resumed task gets each answer back at the right call. Defaults to subject. Each key is asked at most once per try. Set it when the subject can change between runs, or when the task can ask the same subject more than once, for example one approval per agent tool call (use the call’s id).

class airflow.providers.standard.operators.hitl.HITLOperator(*, subject, options, body=None, defaults=None, multiple=False, params=None, notifiers=None, assigned_users=None, response_timeout=None, **kwargs)[source]

Bases: airflow.providers.standard.version_compat.BaseOperator

Base class for all Human-in-the-loop Operators to inherit from.

Parameters:
  • subject (str) – Headline/subject presented to the user for the interaction task.

  • options (list[str]) – List of options that the an user can select from to complete the task.

  • body (str | None) – Descriptive text (with Markdown support) that gives the details that are needed to decide.

  • defaults (str | list[str] | None) – The default options and the options that are taken if timeout is passed.

  • multiple (bool) – Whether the user can select one or multiple options.

  • params (airflow.sdk.definitions.param.ParamsDict | dict[str, Any] | None) – dictionary of parameter definitions that are in the format of Dag params such that a Form Field can be rendered. Entered data is validated (schema, required fields) like for a Dag run and added to XCom of the task result.

  • response_timeout (datetime.timedelta | None) – Maximum time to wait for a human response. On Airflow 3.3+ this is enforced by the scheduler’s awaiting_input timeout sweep; on older versions it is passed to the triggerer. This is separate from execution_timeout, which controls the execution phase before the task starts waiting. If not set, no timeout is applied to the human response wait.

template_fields: collections.abc.Collection[str] = ('subject', 'body')[source]
response_timeout = None[source]
subject[source]
body = None[source]
options[source]
defaults[source]
multiple = False[source]
params: airflow.sdk.definitions.param.ParamsDict[source]
notifiers: collections.abc.Sequence[airflow.sdk.bases.notifier.BaseNotifier][source]
assigned_users[source]
hitl_summary_extra: dict[str, Any][source]
property hitl_summary: dict[str, Any][source]

Summary of the Human-in-the-loop request, for listeners/observability.

A property so the subject/body template fields are read after rendering, not captured as un-rendered Jinja in __init__. Each access builds a fresh snapshot, so mutating the returned dict has no effect — runtime code and subclasses extend the summary by adding entries to hitl_summary_extra instead.

validate_options()[source]

Validate the options attribute of the instance.

Raises:

ValueError: If options is empty.

validate_params()[source]

Validate the params attribute of the instance.

Note: Value validation (e.g., required fields, schema) is intentionally skipped here because HITLOperator params represent form fields that are filled by a human at runtime. Values do not exist at Dag parse time, so validating them in __init__ would cause a ParamValidationError for any param without a default. Value validation happens in validate_params_input after the human submits the form.

Raises:

ValueError: If "_options" key is present in params, which is not allowed.

validate_defaults()[source]

Validate whether the given defaults pass the following criteria.

  1. Default options should be the subset of options.

  2. When multiple is False, there should only be one option.

execute(context)[source]

Write the Human-in-the-loop request, then wait for a user response.

On Airflow 3.3+ the task waits in the awaiting_input state with no trigger or triggerer involved; on older versions it defers to HITLTrigger. Either way it resumes in execute_complete once a response (or timeout default) arrives.

property serialized_params: dict[str, dict[str, Any]][source]
execute_complete(context, event)[source]
process_trigger_event_error(event)[source]
validate_chosen_options(chosen_options)[source]

Check whether user provide valid response.

validate_params_input(params_input)[source]

Check whether user provide valid params input.

Generate a URL link to the “required actions” page for a specific task instance.

This URL includes query parameters based on allowed options and parameters.

Args:

task_instance: The task instance to generate the link for. base_url: Optional base URL to use. Defaults to api.base_url from config. options: Optional subset of allowed options to include in the URL. params_input: Optional subset of allowed params to include in the URL.

Raises:

ValueError: If any provided option or parameter is invalid. ValueError: If no base_url can be determined.

Returns:

The full URL pointing to the required actions page with query parameters.

Generate a “required actions” page URL from a task context.

Delegates to generate_link_to_ui using the task and task_instance extracted from the provided context.

Args:

context: The Airflow task context containing ‘task’ and ‘task_instance’. base_url: Optional base URL to use. options: Optional list of allowed options to include. params_input: Optional dictionary of allowed parameters to include.

Returns:

The full URL pointing to the required actions page with query parameters.

class airflow.providers.standard.operators.hitl.ApprovalOperator(*, ignore_downstream_trigger_rules=False, fail_on_reject=False, **kwargs)[source]

Bases: HITLOperator, airflow.providers.common.compat.sdk.SkipMixin

Human-in-the-loop Operator that has only ‘Approval’ and ‘Reject’ options.

inherits_from_skipmixin = True[source]

Used to determine if an Operator is inherited from SkipMixin or its subclasses (e.g., BranchMixin).

FIXED_ARGS = ['options', 'multiple'][source]
APPROVE = 'Approve'[source]
REJECT = 'Reject'[source]
ignore_downstream_trigger_rules = False[source]
fail_on_reject = False[source]
execute_complete(context, event)[source]
class airflow.providers.standard.operators.hitl.HITLBranchOperator(*, options_mapping=None, **kwargs)[source]

Bases: HITLOperator, airflow.providers.common.compat.sdk.BranchMixIn

BranchOperator based on Human-in-the-loop Response.

inherits_from_skipmixin = True[source]

Used to determine if an Operator is inherited from SkipMixin or its subclasses (e.g., BranchMixin).

options_mapping[source]
validate_options_mapping()[source]

Validate that options_mapping keys match self.options and all values are strings.

Raises:

ValueError: If any key is not in self.options or any value is not a string.

execute_complete(context, event)[source]

Execute the operator and branch based on chosen options.

class airflow.providers.standard.operators.hitl.HITLEntryOperator(**kwargs)[source]

Bases: HITLOperator

Human-in-the-loop Operator that is used to accept user input through TriggerForm.

OK = 'OK'[source]

Was this entry helpful?