Jim Wisniewski
07/31/2025, 9:13 PMJim Wisniewski
07/31/2025, 9:19 PMTypeError: cannot pickle '_thread.lock' object, though the error message does not bother to say what object or where it is. As near as I can tell it’s something inside the Flow object that’s included in the return value from prefect.context.serialize_context, and then the error happens when trying to pickle that return value. I don’t think this is something my code is setting directly, so I’m not sure what we could change to avoid itJim Wisniewski
07/31/2025, 9:19 PMretries=1 specified on the flow decorator, and it runs as expected when retryingJim Wisniewski
08/08/2025, 3:50 PMMultiprocessTaskRunner the first time, which then fails with the pickle error I described above. When the flow is retried, the task runner instead becomes ThreadPoolTaskRunner. @Ryan Peden - apologies for the direct ping but I believe prefect-multiprocess is your library, is this something you recognize by any chance? (See also this thread; I’ll see if I can repro this more minimally.)Ryan Peden
08/09/2025, 2:37 AMThreadPoolTaskRunner during the retry because that's what runs inside each of the process workers.
That part seems expected at least, but I'll try to reproduce the error you're seeing so I can improve the library. I'll test it out tomorrow and see if I can push a fix to PyPI.
Are you using the task runner with Prefect 2, or are you using the prerelease version that works with Prefect 3?Jim Wisniewski
08/09/2025, 4:17 AMJim Wisniewski
08/09/2025, 4:17 AMprefect-multiprocess = "0.2.0b3"Jim Wisniewski
08/09/2025, 4:17 AMfrom time import sleep
from ddtrace import tracer
from prefect import context, flow, get_run_logger, task
from prefect_multiprocess.task_runners import MultiprocessTaskRunner
@tracer.wrap(name="prefect.task", resource="mp_test_task")
def mp_test_task(val: int) -> int:
sleep(1)
return val
@flow(retries=1, retry_delay_seconds=2, task_runner=MultiprocessTaskRunner)
@tracer.wrap(name="prefect.flow", resource="mp_test_flow")
def mp_test_flow(count: int) -> list:
task_runner = context.get_run_context().task_runner
get_run_logger().info(f"*** {task_runner=}")
vals = list(range(count))
task_runs = task(mp_test_task).map(vals)
results = task_runs.result()
return resultsJim Wisniewski
08/09/2025, 4:19 AMcannot pickle error is probably “real” in that sense. But the task runner being a different class is not something I expected, and it failing over silently made it harder to debug, because from the outside it looked like the flow was working, just slowlyJim Wisniewski
08/09/2025, 4:22 AMRyan Peden
08/09/2025, 7:42 PMprefect-multiprocess if you'd like - that'll make it easy to track this.Jim Wisniewski
08/09/2025, 7:43 PMJim Wisniewski
08/09/2025, 7:46 PMRyan Peden
08/09/2025, 7:53 PMJim Wisniewski
08/11/2025, 7:35 PM