Skip to content

session-memory hook duplicates assistant messages when thinking is stripped #92563

Description

@redcrowst2-afk

Bug Description

When using a model with thinking/reasoning enabled, the session JSONL saves two copies of every assistant response:

  1. The RAW version (with thinking blocks + text)
  2. A cleaned version (text only, with parent pointing to the first message)

The session-memory hook then reads both messages via getRecentSessionContent()extractTextMessageContent(), which extracts the same text from each. This results in every assistant line appearing twice in memory files.

Root Cause

In the message_end event handler, when an assistant response contains thinking content, two messages are persisted:

  • First: { role: "assistant", content: [{ type: "thinking", ... }, { type: "text", ... }] } (raw)
  • Second: { role: "assistant", content: [{ type: "text", ... }] } (cleaned, parent=first)

The session-memory hooks extractTextMessageContent() treats both as valid text messages, producing duplicate assistant: ... lines.

Steps to Reproduce

  1. Use a model with thinking/reasoning enabled (e.g., DeepSeek)
  2. Have a conversation with the agent
  3. Run /new or /reset to trigger session-memory save
  4. Open the generated memory file — each assistant message appears twice

Expected Behavior

Each unique assistant message should appear only once in the memory file.

Environment

  • OpenClaw version: 2026.6.6 (also confirmed on 2026.6.1)
  • OS: macOS (Darwin arm64)
  • Model: deepseek/deepseek-v4-flash (with thinking enabled)

Suggested Fix

The cleanest fix is at the storage layer — the message_end handler should not persist both the raw and cleaned versions. Only one should be saved.

As a temporary workaround, the session-memory hooks getRecentSessionContent() can be patched to skip consecutive assistant messages with identical text:

let lastAssistantText = null;
// ... inside the loop:
if (role === "assistant" && text === lastAssistantText) continue;
allMessages.push(`${role}: ${text}`);
if (role === "assistant") lastAssistantText = text;

But this is a band-aid — the real fix should prevent duplicate storage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions