openclaw - 💡(How to fix) Fix [Matrix Plugin] Messages replayed as new after gateway restart — dedupe state not persisted across restarts [3 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#57277Fetched 2026-04-08 01:51:41
View on GitHub
Comments
3
Participants
2
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
commented ×3closed ×1locked ×1

After a gateway restart (triggered by openclaw update from 2026.3.12 to 2026.3.28), the Matrix plugin re-processed every recent message from all joined rooms as if they were new inbound messages. The agent responded to each one again, causing:

  • Duplicate responses sent to all active rooms (8+ rooms affected)
  • Significant wasted tokens (Opus-tier model)
  • Confused conversation context (duplicate Q&A pairs in session history)
  • User confusion from receiving re-answers to already-resolved questions

Root Cause

Layer 1 — Core inbound dedupe (in-memory only):

The core inboundDedupeCache in src/auto-reply/reply/inbound-dedupe.ts is a resolveGlobalDedupeCache with 20-minute TTL and max 5000 entries. This cache is purely in-memory and is cleared on process restart. resetInboundDedupe() is exported and may be called during startup.

Layer 2 — Matrix plugin persistent storage:

The Matrix plugin writes inbound-dedupe.json and bot-storage.json (containing savedSync.nextBatch) to disk under ~/.openclaw/matrix/accounts/default/<homeserver>/<tokenHash>/.

However, on restart after update, the storage was recreated fresh:

// storage-meta.json — created at restart time, not preserved from before
{
  "createdAt": "2026-03-29T19:51:06.999Z"
}
// bot-storage.json — unclean shutdown flag
{
  "cleanShutdown": false,
  "savedSync": { "nextBatch": "s852_..." }
}

A migration-snapshot.json was also created with "trigger": "gateway-startup", suggesting the update path recreated storage rather than preserving existing state.

Result: When the Matrix SDK re-synced after restart, the dedupe cache (both in-memory core and plugin persistent) was empty. All backfilled messages from the sync passed the dedupe check and were dispatched as new inbound messages.

Fix Action

Fix / Workaround

Result: When the Matrix SDK re-synced after restart, the dedupe cache (both in-memory core and plugin persistent) was empty. All backfilled messages from the sync passed the dedupe check and were dispatched as new inbound messages.

Code Example

// storage-meta.json — created at restart time, not preserved from before
{
  "createdAt": "2026-03-29T19:51:06.999Z"
}

---

// bot-storage.json — unclean shutdown flag
{
  "cleanShutdown": false,
  "savedSync": { "nextBatch": "s852_..." }
}

---

~/.openclaw/matrix/accounts/default/<homeserver>/<tokenHash>/
├── bot-storage.json          # cleanShutdown: false, has savedSync.nextBatch
├── inbound-dedupe.json       # 31 entries (all post-restart, none pre-restart)
├── storage-meta.json         # createdAt matches restart time, not original setup
├── legacy-crypto-migration.json
└── crypto/bot-sdk.json

~/.openclaw/matrix/migration-snapshot.json  # trigger: gateway-startup
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Summary

After a gateway restart (triggered by openclaw update from 2026.3.12 to 2026.3.28), the Matrix plugin re-processed every recent message from all joined rooms as if they were new inbound messages. The agent responded to each one again, causing:

  • Duplicate responses sent to all active rooms (8+ rooms affected)
  • Significant wasted tokens (Opus-tier model)
  • Confused conversation context (duplicate Q&A pairs in session history)
  • User confusion from receiving re-answers to already-resolved questions

Root cause analysis

Layer 1 — Core inbound dedupe (in-memory only):

The core inboundDedupeCache in src/auto-reply/reply/inbound-dedupe.ts is a resolveGlobalDedupeCache with 20-minute TTL and max 5000 entries. This cache is purely in-memory and is cleared on process restart. resetInboundDedupe() is exported and may be called during startup.

Layer 2 — Matrix plugin persistent storage:

The Matrix plugin writes inbound-dedupe.json and bot-storage.json (containing savedSync.nextBatch) to disk under ~/.openclaw/matrix/accounts/default/<homeserver>/<tokenHash>/.

However, on restart after update, the storage was recreated fresh:

// storage-meta.json — created at restart time, not preserved from before
{
  "createdAt": "2026-03-29T19:51:06.999Z"
}
// bot-storage.json — unclean shutdown flag
{
  "cleanShutdown": false,
  "savedSync": { "nextBatch": "s852_..." }
}

A migration-snapshot.json was also created with "trigger": "gateway-startup", suggesting the update path recreated storage rather than preserving existing state.

Result: When the Matrix SDK re-synced after restart, the dedupe cache (both in-memory core and plugin persistent) was empty. All backfilled messages from the sync passed the dedupe check and were dispatched as new inbound messages.

Steps to reproduce

  1. Have an active Matrix setup with multiple rooms and recent messages
  2. Run openclaw update (or trigger a gateway restart via SIGUSR1)
  3. Observe that the agent re-processes and re-responds to all recent messages from before the restart

Expected behavior

  1. The Matrix sync token (nextBatch) and dedupe state should persist across gateway restarts
  2. If storage must be recreated, the plugin should either:
    • Restore the previous sync token so only truly new messages are processed, OR
    • Mark all messages from before the restart as "already seen" during initial sync, OR
    • Apply a time-based filter (e.g., only process messages received after the restart timestamp)
  3. The cleanShutdown flag should be set to true on graceful SIGUSR1 restarts (as opposed to crashes)

Environment

  • OpenClaw: 2026.3.12 → 2026.3.28 (npm global install)
  • Matrix plugin: @openclaw/matrix 2026.3.13 (extension under ~/.openclaw/extensions/matrix/)
  • Homeserver: Synapse (self-hosted)
  • OS: Ubuntu 24.04 LTS
  • Restart method: gateway update.run → SIGUSR1

Relevant file layout (post-restart)

~/.openclaw/matrix/accounts/default/<homeserver>/<tokenHash>/
├── bot-storage.json          # cleanShutdown: false, has savedSync.nextBatch
├── inbound-dedupe.json       # 31 entries (all post-restart, none pre-restart)
├── storage-meta.json         # createdAt matches restart time, not original setup
├── legacy-crypto-migration.json
└── crypto/bot-sdk.json

~/.openclaw/matrix/migration-snapshot.json  # trigger: gateway-startup

Impact

High — data integrity and cost issue. Every gateway restart or update risks:

  • Sending duplicate messages to all active Matrix rooms
  • Burning tokens on re-processing already-handled messages
  • Corrupting session context with duplicate exchanges
  • Eroding user trust (receiving repeat answers is confusing)

Suggested fix

  1. Persist dedupe state across restarts — restore inbound-dedupe.json entries on startup before the Matrix SDK begins its initial sync
  2. Handle SIGUSR1 as graceful shutdown — set cleanShutdown: true before storage is closed so the restart path preserves storage rather than recreating it
  3. Fallback safety net — if fresh sync is unavoidable, pre-populate the dedupe cache with message IDs from the backfill window, or skip processing messages with timestamps before the restart

Related issues

  • #49247 — Matrix sends messages twice (similar dedupe failure, different trigger)

OpenClaw version

2026.3.28

Operating system

Ubuntu 24.04

Install method

npm global

extent analysis

Fix Plan

To address the issue of duplicate responses sent to all active rooms after a gateway restart, we need to implement the following steps:

  • Persist dedupe state across restarts: Restore inbound-dedupe.json entries on startup before the Matrix SDK begins its initial sync.
  • Handle SIGUSR1 as graceful shutdown: Set cleanShutdown: true before storage is closed so the restart path preserves storage rather than recreating it.
  • Fallback safety net: If fresh sync is unavoidable, pre-populate the dedupe cache with message IDs from the backfill window, or skip processing messages with timestamps before the restart.

Here are the concrete steps to implement the fix:

  1. Modify inbound-dedupe.ts:
    • Load existing inbound-dedupe.json entries into the inboundDedupeCache on startup.
    • Example code:

import fs from 'fs'; import path from 'path';

const dedupeCacheFile = 'inbound-dedupe.json'; const dedupeCachePath = path.join('~/.openclaw/matrix/accounts/default/<homeserver>/<tokenHash>/', dedupeCacheFile);

export function loadDedupeCache() { try { const dedupeCacheData = fs.readFileSync(dedupeCachePath, 'utf8'); const dedupeCacheEntries = JSON.parse(dedupeCacheData); // Populate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

  1. The Matrix sync token (nextBatch) and dedupe state should persist across gateway restarts
  2. If storage must be recreated, the plugin should either:
    • Restore the previous sync token so only truly new messages are processed, OR
    • Mark all messages from before the restart as "already seen" during initial sync, OR
    • Apply a time-based filter (e.g., only process messages received after the restart timestamp)
  3. The cleanShutdown flag should be set to true on graceful SIGUSR1 restarts (as opposed to crashes)

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Matrix Plugin] Messages replayed as new after gateway restart — dedupe state not persisted across restarts [3 comments, 2 participants]