|
Hey everybody, I started changing my dags to use @task.external_python(). A couple dags fail with: I have a mixture of normal tasks and external_python tasks. Airflow Version: 3.3.0 If you need more infos I'm happy to provide. Edit: More info
Second error:
|
Replies: 5 comments 4 replies
|
Here is the straight-to-the-point explanation: when you use @task.external_python, Airflow tries to pickle your function to send it over to a separate Python environment. If the function tries to grab variables, helpers, or imports from outside its scope, pickling breaks. To fix it, treat your task as a self-contained box. Everything it needs must live inside the function. ❌ Code that fails from tasks.connections import get_connection # ❌ Import outside task
def my_dag():
def get_config(): # ❌ Local nested function outside task
return {"env": "prod"}
@task.external_python(python="/path/to/venv/bin/python")
def my_task():
cfg = get_config() # 💥 Fails: cannot pickle local function
conn = get_connection() # 💥 Fails: module import mismatch across venvs✅ Code fixed def my_dag():
@task.external_python(python="/path/to/venv/bin/python")
def my_task():
# ✅ Imports go INSIDE the task
from tasks.connections import get_connection
# ✅ Helper logic goes INSIDE the task
cfg = {"env": "prod"}
conn = get_connection()3 golden rules to keep in mind:
Moving the imports and helper logic inside the function will resolve both pickling errors right away. |
|
After your update, I would look less at the body of the failing For {"args": self.op_args, "kwargs": resolved_kwargs}The docs also call out two separate constraints:
That matches the clue you added: the function named in the I would narrow it this way:
@task
def get_config():
return {
"source_conn_id": "my_source",
"target_conn_id": "my_target",
}Then resolve/import inside the external task: @task.external_python(python="/path/to/venv/bin/python")
def load_data(config, rows):
from airflow.hooks.base import BaseHook
source = BaseHook.get_connection(config["source_conn_id"])
# or import your own DB client and use primitive host/user/schema fieldsIf the external environment does not have the same Airflow version installed, avoid
So the practical rule is: keep all imports inside the external function, but also make every upstream XCom, mapped argument, and partial kwarg crossing into If this points you to the bad mapped/partial value, please mark it as answered so other |
|
The connection error comes from touching Airflow objects inside the external task. If you need the venv isolation together with Airflow context, |
|
I figured it out. So I added a normal @task() that takes that XCom and returns it with list(xcom) and pass that to the next task. Thanks everyone! |
I figured it out.
I checked the op_args for the task using the expanded task result and in fact they were
"LazyXComSequence(<unevaluated length>)"which cannot be serialized.So I added a normal @task() that takes that XCom and returns it with list(xcom) and pass that to the next task.
Now it works.
Thanks everyone!