multiprocessing: Can't pickle local object (tested)
Can't pickle local object (on Python 3.13: AttributeError: Can't get local object) means Pool.map was given something it can't send to a worker process. We measured five variants on Python 3.13.5 and the fixes that work.
Why it happens
Pool.map sends each task to a worker process by pickling it. Functions are pickled by name (module plus qualified name) and looked up again in the worker. Anything the worker can't find by name, or any object that can't be serialized, fails. This happens even with the fork start method, which is the default on Linux in 3.13.5 (we checked: get_start_method() returned fork).
1. Nested functions and lambdas
from multiprocessing import Pool
def main():
def square(x):
return x * x
with Pool(2) as pool:
try:
print(pool.map(square, [1, 2, 3]))
except Exception as e:
print(type(e).__name__ + ":", e)
try:
print(pool.map(lambda x: x * x, [1, 2, 3]))
except Exception as e:
print(type(e).__name__ + ":", e)
if __name__ == "__main__":
main()
Output:
AttributeError: Can't get local object 'main.<locals>.square'
AttributeError: Can't get local object 'main.<locals>.<lambda>'
Measured on Python 3.13.5: both raise AttributeError: Can't get local object 'main.<locals>.square'. Older Pythons word it Can't pickle local object (not re-tested here). A lambda at module level fails differently:
from multiprocessing import Pool
square = lambda x: x * x
if __name__ == "__main__":
with Pool(2) as pool:
try:
print(pool.map(square, [1, 2, 3]))
except Exception as e:
print(type(e).__name__ + ":", e)
Output:
PicklingError: Can't pickle <function <lambda> at 0x7fe8a01cf560>: attribute lookup <lambda> on __main__ failed
2. Fix: a top-level function, plus functools.partial for extra arguments
from functools import partial
from multiprocessing import Pool
def power(x, exp):
return x ** exp
if __name__ == "__main__":
with Pool(2) as pool:
print(pool.map(partial(power, exp=2), [1, 2, 3]))
Output:
[1, 4, 9]
Move the function to module level. If you were using a closure or lambda to bind an argument, partial of a top-level function pickles fine, as measured.
3. Bound methods carry the whole object
import threading
from multiprocessing import Pool
class Worker:
def __init__(self):
self.lock = threading.Lock()
def run(self, x):
return x * 2
if __name__ == "__main__":
w = Worker()
with Pool(2) as pool:
try:
print(pool.map(w.run, [1, 2, 3]))
except Exception as e:
print(type(e).__name__ + ":", e)
Output:
TypeError: cannot pickle '_thread.lock' object
Measured: passing w.run pickles w, including its threading.Lock, which raises TypeError: cannot pickle '_thread.lock' object. The same goes for open files, sockets, DB connections and clients. Drop them in __getstate__ and rebuild them in __setstate__:
import threading
from multiprocessing import Pool
class Worker:
def __init__(self):
self.lock = threading.Lock()
def run(self, x):
return x * 2
def __getstate__(self):
state = self.__dict__.copy()
del state["lock"] # not picklable; rebuild in the worker
return state
def __setstate__(self, state):
self.__dict__.update(state)
self.lock = threading.Lock()
if __name__ == "__main__":
with Pool(2) as pool:
print(pool.map(Worker().run, [1, 2, 3]))
Output:
[2, 4, 6]
4. Results and exceptions go back through pickle too
from multiprocessing import Pool
def bad(x):
class Result(Exception):
pass
raise Result(x)
if __name__ == "__main__":
with Pool(2) as pool:
try:
pool.map(bad, [1])
except Exception as e:
print(type(e).__name__ + ":", e)
Output:
MaybeEncodingError: Error sending result: '<multiprocessing.pool.ExceptionWithTraceback object at 0x7f34d8ffea50>'. Reason: 'AttributeError("Can't get local object 'bad.<locals>.Result'")'
Measured: an exception class defined inside the worker function can't be sent back, so instead of your error you get MaybeEncodingError wrapping the pickling failure. Define exception classes, and any classes you return, at module level.
Frequently asked questions
How do I fix Can't pickle local object in multiprocessing?
Move the function out of the enclosing function to module level. Use functools.partial to bind extra arguments; we measured partial(power, exp=2) working with Pool.map.
Can I use a lambda with multiprocessing Pool.map?
No. We measured both a nested lambda (AttributeError: Can't get local object) and a module-level lambda (PicklingError: attribute lookup <lambda> on __main__ failed) failing on Python 3.13.5.
What does cannot pickle '_thread.lock' object mean?
Something you sent to a worker, often self via a bound method, holds a lock. Remove it in __getstate__ and recreate it in __setstate__, which we measured working.
What is MaybeEncodingError: Error sending result?
The worker's return value or exception couldn't be pickled back to the parent. We measured it with an exception class defined inside the worker function.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux (fork start method): nested functions, nested and module-level lambdas, functools.partial, a bound method holding a lock with and without __getstate__, and an unpicklable exception raised in a worker. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.