Skip to content
Latchkey

Python multiprocessing "can't pickle" error in CI

The spawn/forkserver start method serializes the callable and its arguments with pickle to hand them to a worker process. An object that cannot be pickled - a lambda, a local function, a lock, an open file - makes the dispatch fail.

What this error means

A run using multiprocessing or a ProcessPool fails with "TypeError: cannot pickle 'X' object" or "PicklingError: Can't pickle <function <lambda>>". It often only appears on spawn-based platforms.

python
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7f...>:
attribute lookup <lambda> on __main__ failed

Diagnose it: is it the build backend or a missing system library?

Python packaging failures in CI split into build-backend configuration problems and missing system headers. The traceback usually points at the backend even when the real cause is an absent -dev package.

Terminal
python -m pip install --upgrade pip build
python -m build --wheel 2>&1 | tail -40

# a compiler error naming a .h file is a system dependency, not a Python one
#   e.g. "Python.h: No such file" -> python3-dev
#        "openssl/ssl.h"          -> libssl-dev

Common causes

The target or argument is unpicklable

Lambdas, locally-defined functions, open sockets/files, locks, and DB connections cannot be pickled, so they cannot cross the process boundary.

Spawn start method requires picklable everything

On spawn (default on macOS and Windows, used by some CI), the child re-imports the module, so anything passed must round-trip through pickle.

How to fix it

Pass picklable, top-level callables

  1. Replace lambdas and nested functions with module-level functions.
  2. Pass plain data (ids, paths) and reconstruct connections inside the worker.
  3. Move unpicklable resources out of the arguments.
app/jobs.py
def work(item_id):  # top-level, picklable
    conn = connect()  # built inside the worker
    return process(conn, item_id)

Initialize resources per worker

Use a pool initializer to create non-picklable resources in each worker instead of passing them in.

app/jobs.py
from multiprocessing import Pool
with Pool(initializer=setup_worker) as pool:
    pool.map(work, item_ids)

How to prevent it

  • Pass only picklable data to worker processes.
  • Define worker targets at module top level, not inline.
  • Construct connections, locks, and file handles inside the worker.

Frequently asked questions

What causes Python multiprocessing "can't pickle" error in CI?
There are 2 common causes: the target or argument is unpicklable and spawn start method requires picklable everything. Lambdas, locally-defined functions, open sockets/files, locks, and DB connections cannot be pickled, so they cannot cross the process boundary.
How do I fix Python multiprocessing "can't pickle" error in CI?
There are 2 fixes depending on which cause you have: pass picklable, top-level callables and initialize resources per worker. Work through them in order, since the first is the most common.
What does Python multiprocessing "can't pickle" error in CI actually mean?
A run using multiprocessing or a ProcessPool fails with "TypeError: cannot pickle 'X' object" or "PicklingError: Can't pickle <function <lambda>>".
How do I stop Python multiprocessing "can't pickle" error in CI happening again?
Pass only picklable data to worker processes. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card