← Back to blog

ConversationBufferMemory deprecated — LangChain.js migration guide

· Updated 2026-07-11· db0.ailangchainmemoryjavascriptmigration

Short answer: for new LangChain.js applications, treat ConversationBufferMemory and ConversationChain as legacy APIs. LangChain v1 agents keep short-term, thread-level memory in agent state persisted by a checkpointer. Cross-thread, user-level memory uses a separate store. The current JavaScript reference also marks RunnableWithMessageHistory as deprecated since v0.3.

This article covers LangChain.js (JavaScript/TypeScript). The current migration map is:

If your application uses Status Current LangChain.js path
ConversationBufferMemory or ConversationChain Legacy chain memory createAgent with a checkpointer
RunnableWithMessageHistory Deprecated in the current JS reference Agent state with a checkpointer
In-memory MemorySaver Development only A database-backed checkpointer in production
Thread-only state Does not cross conversations A Store for user or application memory

Official LangChain docs and migration references

The important distinction is scope: a checkpointer persists one thread, while a store holds information that must be available across threads. If you only replace ConversationBufferMemory with a checkpointer, you have durable chat history—not automatically extracted user knowledge.

Why the old memory APIs changed

ConversationBufferMemory (v0.1)

The original pattern stored conversation history on a chain:

import { BufferMemory } from "langchain/memory";  // deprecated
const memory = new BufferMemory();
const chain = new ConversationChain({ llm, memory });

The default implementation was in-process and thread-local. Durable storage, user-level scoping, and conflict handling required additional infrastructure. As LangChain moved away from classic chains, the recommended memory model moved into agent or graph state persisted by a checkpointer.

RunnableWithMessageHistory (LCEL era)

This wrapper attached a BaseChatMessageHistory to an LCEL runnable through a session factory. It could use a persistent history implementation, but the application still had to wire session IDs, input keys, output keys, and storage correctly.

The current JavaScript reference marks it deprecated since v0.3. New LangChain agents use state and a checkpointer instead.

LangGraph Checkpointer (current)

The current official answer for thread-level memory. LangChain agents are built on LangGraph, and a checkpointer persists their state so a thread can resume later. But it is not the whole long-term-memory story:

  • langgraph dev can silently ignore custom Checkpointer configuration depending on how you create the agent. State ends up in-memory during development, destroyed on every hot reload. You think you have persistence, but you don't.
  • A checkpointer covers thread-internal state. Cross-session memory (user preferences that persist across conversations) requires a separate Store interface.
  • Requires adopting LangGraph's execution model. If you're using plain LCEL or a custom agent loop, checkpointers don't help.

The root cause: coupling

Every LangChain.js memory solution was tightly coupled to a specific abstraction layer:

BufferMemory          → coupled to ConversationChain
RunnableWithMsgHist   → coupled to LCEL Runnables
Checkpointer          → coupled to LangGraph

When the layer was redesigned, the memory solution broke. This isn't a bug. It's structural.

Think of it like coupling your database schema to your web framework. Nobody rebuilds their Postgres schema when they upgrade Express to Fastify. The storage layer and the application layer are independent concerns. But LangChain's memory has always been part of the application layer, and every time that layer gets redesigned (which is often, and should be), the memory goes with it.

The LangChain team will keep evolving their orchestration. They should. The Hacker News thread "Why we no longer use LangChain" had developers calling the abstractions "5 layers deep." The team is simplifying. That's good. But if your memory lives inside the orchestration, it will keep breaking.

The fix is architectural: decouple the memory layer from the framework.

Coupled:    LangChain version → memory API version (breaks on upgrade)
Decoupled:  LangChain version → (no effect) → memory version

Decoupled memory with db0

db0 is an open-source memory system that stores agent knowledge in SQLite or PostgreSQL, independent of which framework version you're running.

npm install @db0-ai/langchain @langchain/core
import { createDb0 } from "@db0-ai/langchain";
import { ChatOpenAI } from "@langchain/openai";
import { createAgent } from "langchain";

const memory = await createDb0();

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-5.4-mini" }),
  tools: [...memory.tools],  // db0_memory_write, db0_memory_search, db0_memory_list
});

// Run 1 — user states a preference
await agent.invoke({
  messages: [{ role: "user", content: "I prefer TypeScript over Python" }],
});

// Run 2 — agent can recall the preference
const result = await agent.invoke({
  messages: [{ role: "user", content: "What language should I use for this project?" }],
});
// Agent has access to the TypeScript preference from Run 1

The agent gets three tools: db0_memory_write (store a fact with scope and tags), db0_memory_search (semantic search across stored facts), db0_memory_list (list memories by scope).

Memory is stored in SQLite locally. The agent uses LangChain's current createAgent API, while the stored knowledge remains independent of LangChain's orchestration version.

What about contradictions?

The most common memory bug in LangChain apps: the agent accumulates conflicting facts.

Turn 3:  "User prefers Python"
Turn 47: "Actually, I switched to TypeScript"

Both facts are in memory. A similarity search for "preferred language" might return either one. The agent gives inconsistent answers depending on which embedding scores higher.

This isn't hypothetical. The LOCOMO benchmark (Snap Research) tested exactly this scenario across multiple memory systems. All of them showed significant degradation on questions about facts that changed over time. The reason: vector similarity search has no concept of time. "Prefers Python" and "switched to TypeScript" are both semantically close to "preferred language," so the retriever returns both and the model flips a coin.

db0 handles this with superseding:

await harness.memory().write({
  content: "User prefers TypeScript",
  scope: "user",
  supersedes: previousPreferenceId,
});
// "User prefers Python" is preserved for audit but excluded from search

The old fact doesn't disappear. It's kept for history. But memory_search only returns the current fact. No contradictions, deterministic behavior.

LangGraph Store doesn't have this primitive. Mem0 handles it via LLM decision on every write (adds latency and cost, and the decision itself is non-deterministic). db0 does it explicitly.

Chat message history (BufferMemory replacement)

If you want a drop-in for BufferMemory that also extracts facts, db0 provides a BaseChatMessageHistory implementation:

import { Db0ChatMessageHistory } from "@db0-ai/langchain";

const history = new Db0ChatMessageHistory({ harness: memory.harness });

await history.addUserMessage("I always use TypeScript with strict mode");
await history.addAIMessage("Got it! I'll remember that.");

// Facts are extracted automatically — "always use" is a signal word
// Stored as a user-scoped fact, searchable across all future sessions

Unlike BufferMemory, this persists to disk. Unlike RunnableWithMessageHistory, the API is three methods: addUserMessage, addAIMessage, getMessages.

For Python developers

@db0-ai/langchain targets LangChain.js. If you're using Python LangChain/LangGraph, the current production-stable options are:

  • LangGraph PostgresSaver, if you're already in the LangGraph ecosystem
  • Zep, for temporal knowledge graphs with automatic summarization
  • LangMem, LangGraph-native with LLM-driven memory extraction (newer, less battle-tested than the other two)

A Python db0 SDK is on the roadmap but not yet available. I'd rather be honest about this than have you install the package and find no Python support.

The bottom line

The deprecation cycle will continue. LangChain's orchestration will keep evolving, and that's healthy. What's unhealthy is rebuilding your memory layer every time it does.

Decouple them. Store your agent's knowledge in a layer that doesn't know or care what version of LangChain you're running. When v2 ships, your upgrade is npm update @langchain/*, not "rewrite all memory code."

db0 is one way to do this. The architecture matters more than the specific tool.


db0 is open source (MIT). @db0-ai/langchain works with @langchain/core v1+ and @langchain/langgraph v1+.