Skip to content

feat(feishu): persistent message deduplication to prevent duplicate replies#22604

Closed
Amateur0x1 wants to merge 1 commit intoopenclaw:mainfrom
Amateur0x1:fix/feishu-message-dedup-persistence
Closed

feat(feishu): persistent message deduplication to prevent duplicate replies#22604
Amateur0x1 wants to merge 1 commit intoopenclaw:mainfrom
Amateur0x1:fix/feishu-message-dedup-persistence

Conversation

@Amateur0x1
Copy link
Copy Markdown

@Amateur0x1 Amateur0x1 commented Feb 21, 2026

Closes #23369

Problem

Feishu may redeliver the same message multiple times, especially during:

  • WebSocket reconnects
  • OpenClaw restarts
  • Network instability

In one-on-one chat mode, this causes duplicate replies to the same message. The existing in-memory dedup map is lost on restart, so duplicates slip through.

Solution

Implement persistent message deduplication with a dual-layer strategy:

  • Memory cache (fast synchronous path) for hot messages
  • Filesystem store (~/.openclaw/feishu-dedup/) that survives restarts
  • Per-account locking to prevent race conditions in webhook mode
  • Inflight tracking to prevent TOCTOU race where concurrent async calls for the same message both pass the check

Key Design Decisions

  • TTL extended: 30min → 24h for better reliability across restarts
  • Atomic writes: Uses temp file + rename to avoid corruption
  • Probabilistic cleanup: 1% chance when over 10k entries, plus size-based cleanup at 11k to keep latency low
  • Graceful degradation: Memory cache works even if disk fails

Changes

  • extensions/feishu/src/dedup-store.ts: New persistent store implementation with account-level isolation
  • extensions/feishu/src/dedup.ts: Convert to async with persistent store integration
  • extensions/feishu/src/bot.ts: Use new async dedup function

Testing

  • Restart OpenClaw and verify no duplicate processing
  • Simulate WebSocket reconnect
  • Verify memory + disk cache consistency
  • Test concurrent message handling (race condition check)

Fixes duplicate message processing in Feishu DM conversations.

@openclaw-barnacle openclaw-barnacle bot added channel: feishu Channel integration: feishu size: M labels Feb 21, 2026
@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch from a68cb59 to 1642228 Compare February 21, 2026 11:10
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch from 1642228 to ebbb525 Compare February 21, 2026 11:13
@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch from ebbb525 to bd3ba37 Compare February 21, 2026 11:14
@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch from bd3ba37 to 394e1b3 Compare February 21, 2026 11:21
@Amateur0x1 Amateur0x1 closed this Feb 21, 2026
@Amateur0x1 Amateur0x1 reopened this Feb 21, 2026
@Amateur0x1 Amateur0x1 closed this Feb 21, 2026
@Amateur0x1 Amateur0x1 reopened this Feb 21, 2026
@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch 2 times, most recently from 6c46b07 to 1539976 Compare February 21, 2026 11:30
@Amateur0x1 Amateur0x1 closed this Feb 21, 2026
@Amateur0x1 Amateur0x1 reopened this Feb 22, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 22 to 25
// First check memory cache (fast path)
if (processedMessageIds.has(cacheKey)) {
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant memory cache layer: dedup.ts maintains its own processedMessageIds cache, but tryRecordMessagePersistent also maintains memoryCache inside dedup-store.ts

the first check here will miss if the message was added during a previous run (only in disk), then tryRecordMessagePersistent will correctly find it in the persistent store and return false, but then line 35 adds it to processedMessageIds anyway. this works correctly but stores the same data twice in memory

consider removing processedMessageIds from dedup.ts and relying solely on the memoryCache inside dedup-store.ts, or document why both layers are needed

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/dedup.ts
Line: 22-25

Comment:
redundant memory cache layer: `dedup.ts` maintains its own `processedMessageIds` cache, but `tryRecordMessagePersistent` also maintains `memoryCache` inside `dedup-store.ts`

the first check here will miss if the message was added during a previous run (only in disk), then `tryRecordMessagePersistent` will correctly find it in the persistent store and return `false`, but then line 35 adds it to `processedMessageIds` anyway. this works correctly but stores the same data twice in memory

consider removing `processedMessageIds` from `dedup.ts` and relying solely on the `memoryCache` inside `dedup-store.ts`, or document why both layers are needed

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

…eplies

    - Add dedup-store.ts for filesystem-based message deduplication
    - Extend dedup TTL from 30min to 24h for better reliability
    - Survive OpenClaw restarts and WebSocket reconnects
    - Use memory cache + disk storage for performance

    Fixes duplicate message processing when Feishu redelivers messages
    or WebSocket reconnects during one-on-one conversations.
@Amateur0x1 Amateur0x1 force-pushed the fix/feishu-message-dedup-persistence branch from 1539976 to 1b18ba4 Compare February 22, 2026 11:31
@Amateur0x1 Amateur0x1 closed this Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(feishu): persistent message deduplication to prevent duplicate replies

1 participant