Submission checklist
Package (Required)
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
Submission checklist
Package (Required)
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)
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_anthropicinlibs/core/langchain_core/messages/block_translators/anthropic.pybuilds most content blocks as fresh dicts, but the fallbackelsebranch for an unrecognized block type wraps the original block by reference and then pops a key off it: it buildsnew_block["value"] = block(the same dict, not a copy) and then doesnew_block["value"].pop("index"). Sincenew_block["value"]isblock, the original dict living insidemessage.content, that pop deletesindexfrom the message's own stored content as a side effect of reading the read-only-looking.content_blocksproperty.Impact. A message that carries a non-standard content block with an
indexkey silently loses that key the first time.content_blocksis read, e.g. from logging, middleware, or a second unrelated call site. Anything that persists or re-serializesmessage.contentafterward (checkpointers, conversation stores,dumps()) captures the corrupted version.bedrock.py's_convert_to_v1_from_bedrockimports and calls this same function directly, somodel_provider="bedrock"for Claude-backed models inherits the identical bug (confirmed locally).Proposed fix: copy the block before popping
indexoff it, exactly the pattern already accepted for the analogous Bedrock Converse fix (#40022, inbedrock_converse.py) --value = block.copy(), popindexfromvalue, and build thenon_standardwrapper (with the liftedindex) from that copy instead of fromblockdirectly. I already have this implemented with regression tests for both theanthropicandbedrockproviders (message content provably unchanged after one or more.content_blocksreads) and verified locally: fulllangchain-coreunit suite passing (2256 passed),ruff check/ruff format --diff/mypyclean, import-linter clean. Happy to open the PR -- could I be assigned to this issue per the contribution guidelines?System Info
System Information
Package Information
Social handles (optional)
No response