LangChain deprecated memory classes — migration from ConversationBufferMemory (2026) — Fix Guide (2026) | AI Error Hub
Home Providers LangChain Memory migration
LangChain Memory · Migration Severity: Medium HTTP n/a

LangChain ConversationBufferMemory — deprecated, migrate to RunnableWithMessageHistory

The old memory subsystem (<code>ConversationBufferMemory</code>, <code>ConversationSummaryMemory</code>, and friends) was designed for the pre-LCEL chain classes. It is deprecated in current LangChain — the replacement is LCEL-native.

By Ahmed R. · Senior AI Infrastructure Engineer Published 2026-07-26 Verified 2026-07-26

Quick fix (TL;DR)

Resolution: Legacy memory classes are deprecated in favour of RunnableWithMessageHistory (for per-session persistence) and trim_messages (for context management). Migrate by (a) replacing memory usage with a chain wrapped in RunnableWithMessageHistory, (b) replacing ConversationSummaryMemory with an explicit summarization step in your chain, and (c) using trim_messages for windowing and token budgeting.

Real error messages you'll see

These are the exact strings returned by the LangChain framework and its integrations when this error occurs. Copy-paste-searching any of them should land on this page.

Deprecation warning
LangChainDeprecationWarning: ConversationBufferMemory has been deprecated since langchain-core 0.3 and will be removed in 1.0. Use RunnableWithMessageHistory instead: https://python.langchain.com/docs/how_to/migrate_memory
LLMChain deprecated too
LangChainDeprecationWarning: LLMChain has been deprecated since langchain-core 0.1.17. Use the LCEL pipe syntax: prompt | llm.
Import moved / removed
ImportError: cannot import name 'ConversationBufferMemory' from 'langchain.memory'
# The class was moved to langchain_community.memory and then deprecated

Reference

Direct migration mapping

Old (deprecated)New (LCEL-native)
ConversationBufferMemoryRunnableWithMessageHistory + full history
ConversationBufferWindowMemory(k=N)RunnableWithMessageHistory + trim_messages(max_tokens=N * avg)
ConversationSummaryMemoryExplicit summarization chain before injection
ConversationTokenBufferMemorytrim_messages(max_tokens=N, token_counter=llm)
ConversationEntityMemoryCustom chain with entity extraction + retrieval
VectorStoreRetrieverMemoryRegular retrieval chain over history
LLMChain + memoryLCEL chain + RunnableWithMessageHistory
ConversationChainLCEL chain + RunnableWithMessageHistory

Root causes, ranked by frequency

Based on developer reports across LangChain forums, GitHub issues, and Discord community during 2025–2026.

  • 30%
    ConversationBufferMemory + LLMChain in existing code. Both deprecated; needs LCEL rewrite.
  • 18%
    Import paths changed. Old imports fail; class location has moved multiple times.
  • 14%
    ConversationSummaryMemory relied on automatic summarization. New pattern requires an explicit summarize step.
  • 10%
    Custom Memory subclass. Broken by ABC changes in newer versions.
  • 8%
    Windowed memory (k=N). Trim by count vs token count is different in the new API.
  • 7%
    Multi-input chains (LLMChain with multiple keys). Requires more explicit key mapping in LCEL.
  • 8%
    Vector-store memory pattern. Migrate to a standard retrieval chain, not a specialised memory class.
  • 5%
    Legacy agents (initialize_agent) with memory. Full rewrite to create_tool_calling_agent + history wrapper.

Fixes — copy-paste solutions

Fix #1

Migrate ConversationBufferMemory + LLMChain to LCEL + RunnableWithMessageHistory

The most common migration — direct 1:1 replacement.

The old pattern held one Memory instance per conversation. The new pattern wraps the chain with per-session history routing.

migrate_buffer.py
# ============================================================
# BEFORE (deprecated)
# ============================================================
# from langchain.chains import LLMChain
# from langchain.memory import ConversationBufferMemory
# from langchain_openai import ChatOpenAI
# from langchain_core.prompts import PromptTemplate
#
# memory = ConversationBufferMemory(memory_key="history", return_messages=True)
# prompt = PromptTemplate.from_template("History:\n{history}\n\nUser: {input}\nAssistant:")
# chain = LLMChain(llm=ChatOpenAI(model="gpt-4o-mini"), prompt=prompt, memory=memory)
#
# chain.run(input="Hello")
# chain.run(input="What did I say?")
# ============================================================

# AFTER (current, LCEL-native)
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import InMemoryChatMessageHistory

# 1) Build chain with a MessagesPlaceholder where history was
prompt = ChatPromptTemplate.from_messages([
    MessagesPlaceholder("history"),
    ("human", "{input}"),
])
chain = prompt | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()

# 2) Set up session-to-history factory
store = {}
def get_session_history(session_id: str):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

# 3) Wrap
chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)

# 4) Invoke with session config
config = {"configurable": {"session_id": "user_42"}}
chain_with_history.invoke({"input": "Hello"}, config=config)
chain_with_history.invoke({"input": "What did I say?"}, config=config)
# > You said "Hello".
Key differences: history is passed via MessagesPlaceholder not a string variable; the wrapper handles reading/writing; session routing is now explicit via session_id config instead of implicit via memory instance.
Fix #2

Migrate ConversationBufferWindowMemory with trim_messages

Windowing is now a chain step, not a memory config.

The old k=N parameter is replaced by an explicit trim_messages call. Trim by message count, token count, or a custom strategy.

migrate_windowed.py
# BEFORE (deprecated)
# from langchain.memory import ConversationBufferWindowMemory
# memory = ConversationBufferWindowMemory(k=10, return_messages=True)

# AFTER (current)
from langchain_core.messages import trim_messages
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

llm = ChatOpenAI(model="gpt-4o-mini")

# Trim to last N messages OR last N tokens
# Count-based (equivalent to k=10):
trim_by_count = trim_messages(
    max_tokens=10,          # despite the name, this is the count when token_counter=len
    strategy="last",
    token_counter=len,       # use message count instead of tokens
    include_system=True,
    allow_partial=False,
)

# Token-based (better for varying message sizes):
trim_by_tokens = trim_messages(
    max_tokens=4000,
    strategy="last",
    token_counter=llm,        # use the LLM's tokenizer
    include_system=True,
    allow_partial=False,
    start_on="human",
)

# Wire the trimmer INTO the chain — history flows through it before the prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are helpful."),
    MessagesPlaceholder("history"),
    ("human", "{input}"),
])

chain = (
    RunnablePassthrough.assign(history=lambda x: trim_by_tokens.invoke(x["history"]))
    | prompt
    | llm
    | StrOutputParser()
)

# Wrap with RunnableWithMessageHistory as before — the trim happens BEFORE the prompt sees it
chain_with_history = RunnableWithMessageHistory(chain, get_session_history,
                                                input_messages_key="input",
                                                history_messages_key="history")
Token-based trimming is preferable to count-based in almost every case — message sizes vary hugely. Use the LLM as the token_counter for accuracy.
Fix #3

Replace ConversationSummaryMemory with an explicit summarization step

Summarization is now a chain step, not a hidden background operation.

The old memory class silently ran summarization after each turn. The new pattern requires an explicit summarization chain that runs before injection — more code but much more debuggable.

migrate_summary_memory.py
# BEFORE (deprecated)
# from langchain.memory import ConversationSummaryMemory
# memory = ConversationSummaryMemory(llm=ChatOpenAI(), memory_key="history")

# AFTER (current) — explicit summarize-then-inject pattern
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

summariser_llm = ChatOpenAI(model="gpt-4o-mini")
main_llm = ChatOpenAI(model="gpt-4o")

# Summarization sub-chain
summarise_prompt = ChatPromptTemplate.from_messages([
    ("system", "Summarise this conversation in 2-3 sentences, preserving key facts."),
    MessagesPlaceholder("messages"),
])
summarise_chain = summarise_prompt | summariser_llm | StrOutputParser()

def summarise_if_long(messages, threshold=20):
    if len(messages) <= threshold:
        return messages
    # Summarise everything except last N (keep recent context detailed)
    keep = messages[-6:]
    to_summarise = messages[:-6]
    summary = summarise_chain.invoke({"messages": to_summarise})
    return [SystemMessage(content=f"Earlier conversation summary: {summary}")] + keep

# Main chain that summarises history before using it
main_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are helpful."),
    MessagesPlaceholder("history"),
    ("human", "{input}"),
])

chain = (
    RunnablePassthrough.assign(history=lambda x: summarise_if_long(x["history"]))
    | main_prompt
    | main_llm
    | StrOutputParser()
)

chain_with_history = RunnableWithMessageHistory(chain, get_session_history,
                                                input_messages_key="input",
                                                history_messages_key="history")
Summarisation makes chat cheaper for long sessions but adds latency. Only trigger it above a threshold. For most apps, plain trimming is enough — summaries are worth the complexity only for very-long-context conversations.

Prevention checklist

Ship these seven safeguards once and this error stops appearing in your logs.

  • Do not start new code with any ...Memory class — they are deprecated.
  • Use RunnableWithMessageHistory for all per-session persistence.
  • Use trim_messages for windowing / token budgeting inside chains.
  • Migrate LLMChain to LCEL (prompt | llm) simultaneously with memory migration.
  • Pin your LangChain versions — the deprecation timeline may accelerate.
  • Run tests after migration; behaviour is functionally equivalent but edge cases differ.
  • For summarisation, make it explicit — never hidden behind a magic memory class.

Frequently asked questions

Anthropic and the LangChain team target 1.0 for removal but the exact date is subject to change. Watch the LangChain repository release notes. Regardless, migration is the right move today.
Yes — the deprecated classes still work with deprecation warnings. Migrate incrementally: pick one chain at a time. Version-pin LangChain while you migrate to avoid mid-migration breakage.
Rewrite as custom logic in your chain. LCEL is very flexible — anything you did in a Memory hook can be a step in the chain (before or after the prompt/model).
No — history storage is orthogonal. Your Postgres / Redis / DynamoDB backend continues to work. Only the wiring on the LangChain side changes.
LangGraph is a more powerful framework for stateful workflows. For simple chat memory, RunnableWithMessageHistory is simpler. For multi-step agents with complex state, LangGraph is better. You can mix.

Get the weekly AI-error digest

New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.