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",

Reply via email to