This is an automated email from the ASF dual-hosted git repository.

potiuk 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 a93dc92faf7 fix(providers/standard): improve error message for 
non-serializable op_kwargs in PythonVirtualenvOperator (#63270)
a93dc92faf7 is described below

commit a93dc92faf7a603b3537ba6b45a4d0de7dcd34e5
Author: Yoann <[email protected]>
AuthorDate: Wed Mar 11 17:51:56 2026 -0700

    fix(providers/standard): improve error message for non-serializable 
op_kwargs in PythonVirtualenvOperator (#63270)
    
    When render_template_as_native_obj=True, templates like {{ ti }} resolve to
    live Airflow objects that can't be pickled. The resulting PicklingError from
    structlog internals is confusing and doesn't point to the root cause.
    
    Catch serialization failures in _write_args, identify which specific kwargs
    failed, and raise a clear AirflowException naming the bad keys and 
suggesting
    to use string attributes instead.
    
    Closes: #61741
---
 .../airflow/providers/standard/operators/python.py | 27 +++++++++++++++++++---
 .../tests/unit/standard/operators/test_python.py   | 19 +++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git 
a/providers/standard/src/airflow/providers/standard/operators/python.py 
b/providers/standard/src/airflow/providers/standard/operators/python.py
index 153044a5c3f..8d800d8fab1 100644
--- a/providers/standard/src/airflow/providers/standard/operators/python.py
+++ b/providers/standard/src/airflow/providers/standard/operators/python.py
@@ -579,9 +579,30 @@ class _BasePythonVirtualenvOperator(PythonOperator, 
metaclass=ABCMeta):
 
         if self.op_args or self.op_kwargs:
             self.log.info("Use %r as serializer.", self.serializer)
-            file.write_bytes(
-                self.pickling_library.dumps({"args": self.op_args, "kwargs": 
resolve_proxies(self.op_kwargs)})
-            )
+            resolved_kwargs = resolve_proxies(self.op_kwargs)
+            try:
+                file.write_bytes(
+                    self.pickling_library.dumps({"args": self.op_args, 
"kwargs": resolved_kwargs})
+                )
+            except Exception:
+                # Identify which specific kwarg(s) failed to serialize so the
+                # user gets a clear error instead of an opaque PicklingError.
+                bad_keys: list[str] = []
+                for key, value in resolved_kwargs.items():
+                    try:
+                        self.pickling_library.dumps(value)
+                    except Exception:
+                        bad_keys.append(key)
+                if bad_keys:
+                    raise AirflowException(
+                        f"Failed to serialize op_kwargs. The following keys 
contain objects that "
+                        f"cannot be pickled: {bad_keys}. This often happens 
when "
+                        f"render_template_as_native_obj=True and templates 
like '{{{{ ti }}}}' "
+                        f"resolve to live Airflow objects instead of strings. 
Use string "
+                        f"representations or pass only the specific attributes 
you need "
+                        f"(e.g. '{{{{ ti.task_id }}}}', '{{{{ ti.run_id 
}}}}')."
+                    )
+                raise
 
     def _write_string_args(self, file: Path):
         file.write_text("\n".join(map(str, self.string_args)))
diff --git a/providers/standard/tests/unit/standard/operators/test_python.py 
b/providers/standard/tests/unit/standard/operators/test_python.py
index 22497f3646a..437ae89ac26 100644
--- a/providers/standard/tests/unit/standard/operators/test_python.py
+++ b/providers/standard/tests/unit/standard/operators/test_python.py
@@ -1092,6 +1092,25 @@ class BaseTestPythonVirtualenvOperator(BasePythonTest):
         op = self.opcls(task_id="task", python_callable=f, 
**self.default_kwargs())
         copy.deepcopy(op)
 
+    def test_write_args_non_serializable_op_kwargs(self, tmp_path):
+        """Non-serializable op_kwargs should raise AirflowException with 
helpful message."""
+
+        class NonSerializable:
+            def __reduce__(self):
+                raise TypeError("cannot pickle this")
+
+        def f(x):
+            return x
+
+        op = self.opcls(
+            task_id="task",
+            python_callable=f,
+            op_kwargs={"bad_obj": NonSerializable(), "good_obj": "hello"},
+            **self.default_kwargs(),
+        )
+        with pytest.raises(AirflowException, match=r"cannot be 
pickled.*\['bad_obj'\]"):
+            op._write_args(tmp_path / "args.pkl")
+
     def test_virtualenv_serializable_context_fields(self, 
create_task_instance):
         """Ensure all template context fields are listed in the operator.
 

Reply via email to