Skip to content

fix: dedup, idempotent start, and ensurePrivateSession mutex for Feishu WebSocket#73

Open
hmhmdcy wants to merge 1 commit into
HNGM-HP:mainfrom
hmhmdcy:fix/feishu-message-dedup
Open

fix: dedup, idempotent start, and ensurePrivateSession mutex for Feishu WebSocket#73
hmhmdcy wants to merge 1 commit into
HNGM-HP:mainfrom
hmhmdcy:fix/feishu-message-dedup

Conversation

@hmhmdcy

@hmhmdcy hmhmdcy commented Jul 8, 2026

Copy link
Copy Markdown

Problem

When sending a message on Feishu/Lark, the bridge replies twice and creates duplicate OpenCode sessions. Root cause: Feishu WebSocket may deliver the same im.message.receive_v1 event twice.

Three concurrency gaps exist:

  1. No message deduphandleMessage() processes every event without checking message_id
  2. No idempotent startstart() can be called multiple times, creating multiple WSClient connections
  3. TOCTOU race in ensurePrivateSession — Two concurrent handleMessage calls both read chatSessionStore.getSession() before either writes, then both create a new session

Changes

1. src/feishu/client.ts — message_id dedup cache

  • Add a Map<string, number> tracking recent message_id values with 30s TTL
  • In handleMessage(), skip if same message_id seen within TTL
  • Lazy cleanup every 500 entries to prevent memory leak

2. src/feishu/client.ts — idempotent start()

  • Guard start() with early return if connectionState is already 'connected' or 'connecting'

3. src/handlers/p2p.ts — ensurePrivateSession mutex

  • Add _ensureSessionLocks (per-chatId promise lock)
  • Rename original implementation to _ensurePrivateSessionImpl
  • New ensurePrivateSession wrapper: if a lock exists for chatId, await the pending promise instead of entering the critical section

Testing

  • The compiled JS files have been running in production with these fixes applied
  • Verified no regressions in session creation and message flow

…Feishu WebSocket

Three concurrency fixes for duplicate message handling on Feishu/Lark:

1. **client.ts - message dedup**: WebSocket may deliver the same
   im.message.receive_v1 event twice. Cache message_id in a Map with
   30s TTL and skip duplicates.

2. **client.ts - idempotent start()**: Guard against repeated start()
   calls that could create multiple WSClient connections.

3. **p2p.ts - ensurePrivateSession mutex**: Per-chatId promise lock so
   concurrent handleMessage calls don't both enter the session-creation
   critical section (TOCTOU race), creating duplicate sessions.

Without these fixes, every message on Feishu could trigger two replies
and two OpenCode sessions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant