Skip to content

SummarizationMiddleware deletes conversation history when the summary model call fails, inserting the error text as the summary #38867

Description

Submission checklist

  • 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

Reproduction Steps / Example Code (Python)

from typing import Any

from langchain.agents.middleware.summarization import SummarizationMiddleware
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage
from langchain_core.outputs import ChatResult


class RateLimitedModel(BaseChatModel):
    """Simulates a provider that is temporarily down (e.g. 429). Never touches the network."""

    @property
    def _llm_type(self) -> str:
        return "failing-fake"

    def _generate(self, messages: Any, stop: Any = None, run_manager: Any = None, **kwargs: Any) -> ChatResult:
        raise RuntimeError("429 Too Many Requests (simulated transient failure)")


middleware = SummarizationMiddleware(
    model=RateLimitedModel(),
    trigger=("messages", 5),
    keep=("messages", 2),
    token_counter=len,
)

messages = []
for i in range(5):
    messages.append(HumanMessage(content=f"user turn {i}", id=f"h{i}"))
    messages.append(AIMessage(content=f"assistant turn {i}", id=f"a{i}"))

update = middleware.before_model({"messages": messages}, None)

print(isinstance(update["messages"][0], RemoveMessage))  # True: removes ALL messages
print(update["messages"][1].content)
# 'Here is a summary of the conversation to date:\n\nError generating summary: 429 Too Many Requests ...'

Error Message and Stack Trace (if applicable)

No exception is raised, that is the problem. Observed output of the repro:

state update returned by before_model:
  RemoveMessage: ''
  HumanMessage: 'Here is a summary of the conversation to date:\n\nError generating summary: 429 Too Many Req'
  HumanMessage: 'user turn 4'
  AIMessage: 'assistant turn 4'

8 of the 10 original messages are deleted and the inserted "summary" is the provider error string.

Description

When the summarization model call fails for any reason (rate limit, timeout, auth error), SummarizationMiddleware still reports success and rewrites the thread.

_create_summary and _acreate_summary catch every exception from the model call and return the string "Error generating summary: <exception>" as if it were a real summary (libs/langchain_v1/langchain/agents/middleware/summarization.py, around lines 815-822 and 841-848 on current master). before_model then proceeds normally: it emits RemoveMessage(REMOVE_ALL_MESSAGES) plus the new "summary", so the summarized messages are permanently deleted from the thread and the model is fed a summary that is literally the provider error text.

What I expect: a transient summary failure should not destroy state. Either the middleware skips summarization for that turn (keep the original messages, it can retry on a later turn) or the exception propagates so the caller can handle it.

What happens instead: see the repro. 8 of 10 messages are permanently dropped and the model continues with "Error generating summary: 429 Too Many Requests ..." as its memory of the conversation. A single 429 at the wrong moment silently corrupts a long running agent thread, and with a checkpointer the corrupted state is what gets persisted.

A contained fix would be to treat summary failure as "skip summarization this turn" (return no state update) rather than swapping real history for the error string. I have the fix and updated tests ready. I'd like to work on this, could you assign it to me?

System Info

System Information

OS: Windows
OS Version: 10.0.26200
Python Version: 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)]

Package Information

langchain_core: 1.4.9
langchain: 1.3.13
langsmith: 0.10.5
langchain_openai: 1.3.5
langgraph_sdk: 0.4.2

Also reproduced against current master (commit 7bf8fe2), where the same except blocks are present in summarization.py.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugRelated to a bug, vulnerability, unexpected error with an existing featureexternallangchain`langchain` package issues & PRs

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions