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.
Quick fix (TL;DR)
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.
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
LangChainDeprecationWarning: LLMChain has been deprecated since langchain-core 0.1.17. Use the LCEL pipe syntax: prompt | llm.
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) |
|---|---|
ConversationBufferMemory | RunnableWithMessageHistory + full history |
ConversationBufferWindowMemory(k=N) | RunnableWithMessageHistory + trim_messages(max_tokens=N * avg) |
ConversationSummaryMemory | Explicit summarization chain before injection |
ConversationTokenBufferMemory | trim_messages(max_tokens=N, token_counter=llm) |
ConversationEntityMemory | Custom chain with entity extraction + retrieval |
VectorStoreRetrieverMemory | Regular retrieval chain over history |
LLMChain + memory | LCEL chain + RunnableWithMessageHistory |
ConversationChain | LCEL 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
Migrate ConversationBufferMemory + LLMChain to LCEL + RunnableWithMessageHistory
The old pattern held one Memory instance per conversation. The new pattern wraps the chain with per-session history routing.
# ============================================================ # 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".
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.Migrate ConversationBufferWindowMemory with trim_messages
The old k=N parameter is replaced by an explicit trim_messages call. Trim by message count, token count, or a custom strategy.
# 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")
Replace ConversationSummaryMemory with an explicit summarization step
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.
# 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")
Prevention checklist
Ship these seven safeguards once and this error stops appearing in your logs.
- Do not start new code with any
...Memoryclass — they are deprecated. - Use
RunnableWithMessageHistoryfor all per-session persistence. - Use
trim_messagesfor windowing / token budgeting inside chains. - Migrate
LLMChainto 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
RunnableWithMessageHistory is simpler. For multi-step agents with complex state, LangGraph is better. You can mix.Related errors & hubs
Get the weekly AI-error digest
New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.