langchain - ✅(Solved) Fix fix(ollama): _convert_messages_to_ollama_messages mutates caller's list in-place [1 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#36564Fetched 2026-04-08 03:01:07
View on GitHub
Comments
2
Participants
3
Timeline
13
Reactions
0
Timeline (top)
cross-referenced ×4labeled ×3commented ×2referenced ×2

_convert_messages_to_ollama_messages() in chat_models.py modifies the list it receives as a parameter. When it encounters an AIMessage whose response_metadata["output_version"] == "v1", it silently replaces that element in the caller's list:

  messages[idx] = message.model_copy(
      update={
          "content": _convert_from_v1_to_ollama(
              cast("list[types.ContentBlock]", message.content),
              message.response_metadata.get("model_provider"),
          )
      }
  )

The bug is currently hidden because BaseChatModel.invoke() and BaseChatModel.generate() happen to pass a shallow copy of the list to _generate() before it reaches this method. This protection is entirely incidental and undocumented and the method mutates a parameter it doesn't own, as a side-effect.

I can easily patch a fix for it by adding one line i.e messages = list(messages) at the top of _convert_messages_to_ollama_messages.

Please assign this issue so I can patch the one-line fix for it.

Error Message

Error Message and Stack Trace (if applicable)

Root Cause

  messages[idx] = message.model_copy(
      update={
          "content": _convert_from_v1_to_ollama(
              cast("list[types.ContentBlock]", message.content),
              message.response_metadata.get("model_provider"),
          )
      }
  )

The bug is currently hidden because BaseChatModel.invoke() and BaseChatModel.generate() happen to pass a shallow copy of the list to _generate() before it reaches this method. This protection is entirely incidental and undocumented and the method mutates a parameter it doesn't own, as a side-effect.

Fix Action

Fix / Workaround

  • 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.

I can easily patch a fix for it by adding one line i.e messages = list(messages) at the top of _convert_messages_to_ollama_messages.

Please assign this issue so I can patch the one-line fix for it.

PR fix notes

PR #36567: fix(ollama): prevent _convert_messages_to_ollama_messages from mutating caller list

Description (problem / solution / changelog)

Fixes #36564

The method modifies messages[idx] in-place when converting v1 format content. Add messages = list(messages) to create a shallow copy before any mutations.

1 line change in libs/partners/ollama/langchain_ollama/chat_models.py

Changed files

  • libs/partners/ollama/langchain_ollama/chat_models.py (modified, +1/-0)

Code Example

from langchain_core.messages import AIMessage, HumanMessage
from langchain_ollama import ChatOllama

model = ChatOllama(model="gpt-oss:20b-cloud", base_url="http://localhost:11434")

# Simulate a multi-turn conversation where the previous AI response
# was returned in v1 content-block format — the exact condition that
# triggers the buggy branch in _convert_messages_to_ollama_messages.
v1_ai_msg = AIMessage(
    content=[{"type": "text", "text": "The capital of France is Paris."}],
    response_metadata={"output_version": "v1", "model_provider": "ollama"},
)

messages = [
    HumanMessage(content="What is the capital of France?"),
    v1_ai_msg,
    HumanMessage(content="What is one famous landmark there?"),
]

original_ref = messages[1]          # hold a reference to the original object
print("Before:", id(messages[1]))

model._convert_messages_to_ollama_messages(messages)

print("After: ", id(messages[1]))
print()

if messages[1] is not original_ref:
    print("BUG CONFIRMED: messages[1] was silently replaced in-place.")
    print("  original_ref is messages[1] →", original_ref is messages[1])
else:
    print("OK: list was not mutated.")

---



---

messages[idx] = message.model_copy(
      update={
          "content": _convert_from_v1_to_ollama(
              cast("list[types.ContentBlock]", message.content),
              message.response_metadata.get("model_provider"),
          )
      }
  )
RAW_BUFFERClick to expand / collapse

Checked other resources

  • 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

No response

Reproduction Steps / Example Code (Python)

from langchain_core.messages import AIMessage, HumanMessage
from langchain_ollama import ChatOllama

model = ChatOllama(model="gpt-oss:20b-cloud", base_url="http://localhost:11434")

# Simulate a multi-turn conversation where the previous AI response
# was returned in v1 content-block format — the exact condition that
# triggers the buggy branch in _convert_messages_to_ollama_messages.
v1_ai_msg = AIMessage(
    content=[{"type": "text", "text": "The capital of France is Paris."}],
    response_metadata={"output_version": "v1", "model_provider": "ollama"},
)

messages = [
    HumanMessage(content="What is the capital of France?"),
    v1_ai_msg,
    HumanMessage(content="What is one famous landmark there?"),
]

original_ref = messages[1]          # hold a reference to the original object
print("Before:", id(messages[1]))

model._convert_messages_to_ollama_messages(messages)

print("After: ", id(messages[1]))
print()

if messages[1] is not original_ref:
    print("BUG CONFIRMED: messages[1] was silently replaced in-place.")
    print("  original_ref is messages[1] →", original_ref is messages[1])
else:
    print("OK: list was not mutated.")

Error Message and Stack Trace (if applicable)

Description

_convert_messages_to_ollama_messages() in chat_models.py modifies the list it receives as a parameter. When it encounters an AIMessage whose response_metadata["output_version"] == "v1", it silently replaces that element in the caller's list:

  messages[idx] = message.model_copy(
      update={
          "content": _convert_from_v1_to_ollama(
              cast("list[types.ContentBlock]", message.content),
              message.response_metadata.get("model_provider"),
          )
      }
  )

The bug is currently hidden because BaseChatModel.invoke() and BaseChatModel.generate() happen to pass a shallow copy of the list to _generate() before it reaches this method. This protection is entirely incidental and undocumented and the method mutates a parameter it doesn't own, as a side-effect.

I can easily patch a fix for it by adding one line i.e messages = list(messages) at the top of _convert_messages_to_ollama_messages.

Please assign this issue so I can patch the one-line fix for it.

System Info

System Information

OS: Darwin OS Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:29 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6030 Python Version: 3.11.11 (main, Feb 5 2025, 18:58:27) [Clang 19.1.6 ]

Package Information

langchain_core: 1.2.23 langsmith: 0.6.3 langchain_ollama: 1.0.1 langchain_tests: 1.0.2

Optional packages not installed

deepagents deepagents-cli

Other Dependencies

httpx: 0.28.1 jsonpatch: 1.33 numpy: 2.3.3 ollama: 0.6.0 orjson: 3.11.6 packaging: 25.0 pydantic: 2.12.1 pytest: 8.4.2 pytest-asyncio: 0.26.0 pytest-benchmark: 5.1.0 pytest-codspeed: 4.0.0 pytest-recording: 0.13.4 pytest-socket: 0.7.0 pyyaml: 6.0.3 requests: 2.33.0 requests-toolbelt: 1.0.0 rich: 14.1.0 syrupy: 4.9.1 tenacity: 9.1.2 typing-extensions: 4.15.0 uuid-utils: 0.12.0 vcrpy: 7.0.0 zstandard: 0.25.0

extent analysis

TL;DR

The bug can be fixed by adding a line to create a copy of the input list in the _convert_messages_to_ollama_messages method to prevent it from modifying the original list.

Guidance

  • The issue arises from the _convert_messages_to_ollama_messages method modifying the input list when it encounters an AIMessage with response_metadata["output_version"] == "v1".
  • To fix this, add messages = list(messages) at the beginning of the _convert_messages_to_ollama_messages method to create a copy of the input list.
  • This change ensures that the original list passed to the method is not modified, preventing unintended side effects.
  • Verify the fix by running the provided example code and checking that the messages[1] object is not replaced after calling _convert_messages_to_ollama_messages.

Example

def _convert_messages_to_ollama_messages(self, messages):
    messages = list(messages)  # Add this line to create a copy of the input list
    # ... rest of the method implementation ...

Notes

The provided fix assumes that creating a shallow copy of the list is sufficient. If the list contains mutable objects that need to be copied as well, a deeper copy may be necessary.

Recommendation

Apply the workaround by adding the messages = list(messages) line to the _convert_messages_to_ollama_messages method. This fix prevents the method from modifying the original list, ensuring that the code behaves as expected.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING