This is an automated email from the ASF dual-hosted git repository.
weilee pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new fbe7cbef234 fix(providers/standard): remove premature param value
validation in HITLOperator (#64108)
fbe7cbef234 is described below
commit fbe7cbef234b3a14b5f75fcc852d01a1212413f6
Author: Antonio Mello <[email protected]>
AuthorDate: Thu Mar 26 06:51:36 2026 -0300
fix(providers/standard): remove premature param value validation in
HITLOperator (#64108)
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
.../airflow/providers/standard/operators/hitl.py | 9 ++-
.../tests/unit/standard/operators/test_hitl.py | 64 ++++++++++++++--------
2 files changed, 49 insertions(+), 24 deletions(-)
diff --git
a/providers/standard/src/airflow/providers/standard/operators/hitl.py
b/providers/standard/src/airflow/providers/standard/operators/hitl.py
index d3199a7b0a1..bef40d504a3 100644
--- a/providers/standard/src/airflow/providers/standard/operators/hitl.py
+++ b/providers/standard/src/airflow/providers/standard/operators/hitl.py
@@ -147,10 +147,15 @@ class HITLOperator(BaseOperator):
"""
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.
+ ValueError: If ``"_options"`` key is present in ``params``, which
is not allowed.
"""
- self.params.validate()
if "_options" in self.params:
raise ValueError('"_options" is not allowed in params')
diff --git a/providers/standard/tests/unit/standard/operators/test_hitl.py
b/providers/standard/tests/unit/standard/operators/test_hitl.py
index e1ebae5ab00..fb6839e0967 100644
--- a/providers/standard/tests/unit/standard/operators/test_hitl.py
+++ b/providers/standard/tests/unit/standard/operators/test_hitl.py
@@ -139,27 +139,9 @@ class TestHITLOperator:
params=ParamsDict({"input_1": 1}),
)
- @pytest.mark.parametrize(
- ("params", "exc", "error_msg"),
- (
- (ParamsDict({"_options": 1}), ValueError, '"_options" is not
allowed in params'),
- (
- ParamsDict({"param": Param("", type="integer")}),
- ParamValidationError,
- (
- "Invalid input for param param: '' is not of type
'integer'\n\n"
- "Failed validating 'type' in schema:\n"
- " {'type': 'integer'}\n\n"
- "On instance:\n ''"
- ),
- ),
- ),
- )
- def test_validate_params(
- self, params: ParamsDict, exc: type[ValueError |
ParamValidationError], error_msg: str
- ) -> None:
- # validate_params is called during initialization
- with pytest.raises(exc, match=error_msg):
+ def test_validate_params_rejects_options_key(self) -> None:
+ """_options is a reserved key and must not be allowed in params."""
+ with pytest.raises(ValueError, match='"_options" is not allowed in
params'):
HITLOperator(
task_id="hitl_test",
subject="This is subject",
@@ -167,9 +149,47 @@ class TestHITLOperator:
body="This is body",
defaults=["1"],
multiple=False,
- params=params,
+ params=ParamsDict({"_options": 1}),
)
+ @pytest.mark.parametrize(
+ ("params", "expected_key"),
+ [
+ pytest.param(
+ {"my_param": Param(type="string")},
+ "my_param",
+ id="no_default",
+ ),
+ pytest.param(
+ {"my_param": Param("hello", type="string")},
+ "my_param",
+ id="with_default",
+ ),
+ pytest.param(
+ {"param": Param("", type="integer")},
+ "param",
+ id="wrong_value_type",
+ ),
+ ],
+ )
+ def test_param_value_validation_deferred_to_runtime(self, params: dict,
expected_key: str) -> None:
+ """Regression test for #59551.
+
+ HITLOperator params are form fields filled by a human at runtime.
+ Value validation (required, schema) must NOT happen in __init__ — it is
+ deferred to ``validate_params_input`` after the human submits the form.
+ """
+ op = HITLOperator(
+ task_id="hitl_test",
+ subject="This is subject",
+ options=["1", "2"],
+ body="This is body",
+ defaults=["1"],
+ multiple=False,
+ params=params,
+ )
+ assert expected_key in op.params
+
def test_validate_defaults(self) -> None:
hitl_op = HITLOperator(
task_id="hitl_test",