Skip to content

summarizationMiddleware leads message order unsupported by Gemini #9272

@crishoj

Description

@crishoj

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 read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Example Code

import "dotenv/config"
import { createAgent, HumanMessage, SystemMessage, summarizationMiddleware, tool } from "langchain"
import { MemorySaver } from "@langchain/langgraph"
import { ChatGoogleGenerativeAI } from "@langchain/google-genai"
import { z } from "zod"

const model = new ChatGoogleGenerativeAI({
  model: "models/gemini-2.5-flash-preview-09-2025",
  temperature: 0,
  apiKey: process.env.GEMINI_API_KEY,
  baseUrl: "https://generativelanguage.googleapis.com",
})

const searchTool = tool(
  ({ query }: { query: string }) => {
    console.log(`Searching for: ${query}`)
    return `Found results for: ${query}`
  },
  {
    name: "search",
    description: "Search for information",
    schema: z.object({
      query: z.string().describe("The search query"),
    }),
  }
)

const agent = createAgent({
  model,
  tools: [searchTool],
  checkpointer: new MemorySaver(),
  middleware: [
    summarizationMiddleware({
      model: model,
      maxTokensBeforeSummary: 100,
      messagesToKeep: 10,
    })
  ],
})

const threadId = "bug-reproduction"

console.log("\n=== Message 1: Hi (with system message) ===")
await agent.invoke(
  { messages: [new SystemMessage("You are a helpful assistant."), new HumanMessage("Hi")] },
  { configurable: { thread_id: threadId } }
)

console.log("\n=== Message 2: Search for cats ===")
await agent.invoke(
  { messages: [new HumanMessage("Search for cats")] },
  { configurable: { thread_id: threadId } }
)

console.log("\n=== Message 3: Search for dogs ===")
await agent.invoke(
  { messages: [new HumanMessage("Search for dogs")] },
  { configurable: { thread_id: threadId } }
)

console.log("\n=== Message 4: Search for birds (should trigger summarization and fail) ===")
try {
  await agent.invoke(
    { messages: [new HumanMessage("Search for birds")] },
    { configurable: { thread_id: threadId } }
  )
  console.log("✓ Success - no error (bug not reproduced)")
} catch (error: any) {
  if (error.message.includes("function call turn comes immediately after")) {
    console.log("\n✗ BUG REPRODUCED!")
    console.log("Error:", error.message)
  } else {
    console.log("✗ Different error:", error.message)
  }
}

Error Message and Stack Trace (if applicable)

[GoogleGenerativeAI Error]: Error fetching from https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent: [400 Bad Request] Please ensure that function call turn comes immediately after a user turn or after a function response turn.

Description

After summarization with messagesToKeep: 10, the preserved messages
start with an AI message that has tool calls, without its preceding human message.

Gemini requires: humanai (with tool calls) → tool
But gets after summarization: systemai (with tool calls) → tool

I believe this should be handled in summarizationMiddleware.findSafeCutoff().

It ensures tool call/response pairs stay together, but doesn't ensure that
preserved messages don't start with an AI message containing tool calls.

System Info

LangChain v1.0.1 on macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions