Skip to content

core: _convert_to_v1_from_anthropic mutates message.content when lifting index off an unrecognized block #40077

Description

Submission checklist

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

Same broader defect class raised in #40001 ("AIMessage.content_blocks mutates message.content in the Anthropic, Bedrock and Google translators"), which explicitly identified this exact Anthropic-translator case. #40001 was closed as completed after #40022 (bedrock_converse fix) and #40023 (google-genai fix) merged, but neither of those touches anthropic.py -- this specific half was never fixed. bedrock.py's _convert_to_v1_from_bedrock() imports and calls _convert_to_v1_from_anthropic() directly, so model_provider="bedrock" (for Claude-backed models) inherits the same bug.

Reproduction Steps / Example Code (Python)

from copy import deepcopy

from langchain_core.messages import AIMessage

message = AIMessage(
    content=[{"type": "custom", "payload": "value", "index": 3}],
    response_metadata={"model_provider": "anthropic"},
)
original = deepcopy(message.content)

first = message.content_blocks  # first read

print("before:", original)
print("after: ", message.content)
assert message.content == original  # fails: `index` is gone

Error Message and Stack Trace (if applicable)

N/A -- not an exception, silent data corruption.

AssertionError: assert [{'type': 'custom', 'payload': 'value'}] == [{'type': 'custom', 'payload': 'value', 'index': 3}]

Description

_convert_to_v1_from_anthropic in libs/core/langchain_core/messages/block_translators/anthropic.py builds most content blocks as fresh dicts, but the fallback else branch for an unrecognized block type wraps the original block by reference and then pops a key off it: it builds new_block["value"] = block (the same dict, not a copy) and then does new_block["value"].pop("index"). Since new_block["value"] is block, the original dict living inside message.content, that pop deletes index from the message's own stored content as a side effect of reading the read-only-looking .content_blocks property.

Impact. A message that carries a non-standard content block with an index key silently loses that key the first time .content_blocks is read, e.g. from logging, middleware, or a second unrelated call site. Anything that persists or re-serializes message.content afterward (checkpointers, conversation stores, dumps()) captures the corrupted version. bedrock.py's _convert_to_v1_from_bedrock imports and calls this same function directly, so model_provider="bedrock" for Claude-backed models inherits the identical bug (confirmed locally).

Proposed fix: copy the block before popping index off it, exactly the pattern already accepted for the analogous Bedrock Converse fix (#40022, in bedrock_converse.py) -- value = block.copy(), pop index from value, and build the non_standard wrapper (with the lifted index) from that copy instead of from block directly. I already have this implemented with regression tests for both the anthropic and bedrock providers (message content provably unchanged after one or more .content_blocks reads) and verified locally: full langchain-core unit suite passing (2256 passed), ruff check/ruff format --diff/mypy clean, import-linter clean. Happy to open the PR -- could I be assigned to this issue per the contribution guidelines?

System Info

System Information

OS: Darwin
OS Version: Darwin Kernel Version 25.5.0: Tue Jun 9 22:26:46 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T8103
Python Version: 3.14.6 (main, Jun 10 2026, 10:03:53) [Clang 17.0.0 (clang-1700.6.4.2)]

Package Information

langchain_core: 1.6.1
langsmith: 0.11.1
langchain_protocol: 0.0.18
langchain_tests: 1.1.9
langchain_text_splitters: 1.1.2

Social handles (optional)

No response

Metadata

Metadata

Labels

anthropic`langchain-anthropic` package issues & PRsbugRelated to a bug, vulnerability, unexpected error with an existing featurecore`langchain-core` package issues & PRsexternal

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions