Environment
@larksuiteoapi/node-sdk: 1.71.1 (also reproduced on 1.70.0)
- Runtime: Bun 1.3.x / Node-compatible, macOS
- Mode: WSClient long connection (
new WSClient({ appId, appSecret, domain: Domain.Feishu }).start({ eventDispatcher }))
- Event subscribed:
im.message.receive_v1 via new EventDispatcher({}).register({ 'im.message.receive_v1': handler })
- domain: Feishu (open.feishu.cn), self-built app
Summary
Under load — when several im.message.receive_v1 events arrive in close succession while the app is busy handling a previous event (e.g. making outbound im/v1/messages API calls to reply) — the WSClient occasionally drops an event entirely:
- The Feishu event delivery log (Developer Console → 事件与回调 / event log) shows the event was pushed with
success / httpCode: 200 / hookType: 4 (long connection). So Feishu delivered it and the transport ACKed it.
- But the registered
im.message.receive_v1 handler is never invoked for that event.
- Even with
loggerLevel set to debug, the SDK's own [ws] "receive message, message_type: event; message_id: ..." debug line does not appear for the dropped event.
- No
disconnect / error event is emitted, so application-level health checks based on reconnection never trigger.
The event is acknowledged at the transport layer but lost before reaching the dispatcher/handler.
Impact
For a chat bot on long connection, this means user messages are silently not processed (no reply) with no error surfaced anywhere in the SDK. Observed drop rate rises sharply when messages arrive while the app is mid-processing a previous one.
Reproduction (observed)
- Long-connection WSClient with a single
im.message.receive_v1 handler that does non-trivial async work (streaming reply via multiple PUT im/v1/messages/{id} edits).
- Send several messages to the bot in quick succession (so a new event arrives while the previous is still being handled / while outbound API calls are in flight).
- Compare:
- Feishu Console event log: N events, all SUCCESS.
- SDK debug log
[ws] receive message lines: fewer than N.
- Handler invocations: match the SDK debug count, i.e. the missing events never reach the handler.
Concrete instance (message ids redacted): Feishu pushed 6 im.message.receive_v1 events (all SUCCESS in the console within ~20s); the SDK produced only 3 [ws] receive message debug lines; the other 3 were never dispatched and never logged. No disconnect fired; the connection kept working for subsequent events.
Expected
Every event Feishu delivers + ACKs over the long connection should either be dispatched to the registered handler, or — if it cannot be — surfaced as an error/warn log or a recoverable event, so the application can react (e.g. reconcile). Silent loss with a transport-level SUCCESS is the worst case.
Questions
- Is there internal back-pressure / a bounded queue in the WSClient receive path that drops frames when the handler is slow or the event loop is busy?
- Is there a recommended way to detect or reconcile dropped long-connection events (short of periodically polling
im/v1/messages)?
Workaround currently in use
Periodic reconciliation: on activity, poll GET /im/v1/messages for active chats and re-feed any message not yet processed (dedup by message_id). This recovers dropped messages but shouldn't be necessary.
Environment
@larksuiteoapi/node-sdk: 1.71.1 (also reproduced on 1.70.0)new WSClient({ appId, appSecret, domain: Domain.Feishu }).start({ eventDispatcher }))im.message.receive_v1vianew EventDispatcher({}).register({ 'im.message.receive_v1': handler })Summary
Under load — when several
im.message.receive_v1events arrive in close succession while the app is busy handling a previous event (e.g. making outboundim/v1/messagesAPI calls to reply) — the WSClient occasionally drops an event entirely:success/httpCode: 200/hookType: 4(long connection). So Feishu delivered it and the transport ACKed it.im.message.receive_v1handler is never invoked for that event.loggerLevelset todebug, the SDK's own[ws] "receive message, message_type: event; message_id: ..."debug line does not appear for the dropped event.disconnect/errorevent is emitted, so application-level health checks based on reconnection never trigger.The event is acknowledged at the transport layer but lost before reaching the dispatcher/handler.
Impact
For a chat bot on long connection, this means user messages are silently not processed (no reply) with no error surfaced anywhere in the SDK. Observed drop rate rises sharply when messages arrive while the app is mid-processing a previous one.
Reproduction (observed)
im.message.receive_v1handler that does non-trivial async work (streaming reply via multiplePUT im/v1/messages/{id}edits).[ws] receive messagelines: fewer than N.Concrete instance (message ids redacted): Feishu pushed 6
im.message.receive_v1events (all SUCCESS in the console within ~20s); the SDK produced only 3[ws] receive messagedebug lines; the other 3 were never dispatched and never logged. No disconnect fired; the connection kept working for subsequent events.Expected
Every event Feishu delivers + ACKs over the long connection should either be dispatched to the registered handler, or — if it cannot be — surfaced as an error/warn log or a recoverable event, so the application can react (e.g. reconcile). Silent loss with a transport-level SUCCESS is the worst case.
Questions
im/v1/messages)?Workaround currently in use
Periodic reconciliation: on activity, poll
GET /im/v1/messagesfor active chats and re-feed any message not yet processed (dedup bymessage_id). This recovers dropped messages but shouldn't be necessary.