fix(feishu): media/file sends in topic groups fail with 99992402 (invalid receive_id_type=thread_id) - #55067
Conversation
…2402) Feishu CreateMessage only accepts receive_id_type of open_id/user_id/union_id/email/chat_id. The adapter's no-reply-anchor branch sent receive_id_type="thread_id", which the API rejects with [99992402] field validation failed — silently dropping media/file attachments in topic groups (and stale-thread auto-resume sends). Two changes that fix the whole class, not just .md files: - gateway/platforms/base.py: _thread_metadata_for_source now carries a reply_to_message_id anchor for Feishu threads, so media/file sends (which otherwise reach the adapter with no reply target) can use the supported ReplyMessage + reply_in_thread path and land inside the topic. Telegram/other-platform metadata is untouched. - plugins/platforms/feishu/adapter.py: when no reply anchor is available, _send_raw_message degrades to chat_id delivery instead of emitting the invalid thread_id receive_id_type, so the message still reaches the conversation rather than being dropped. Relates to NousResearch#39526, NousResearch#35576.
- Replace the change-detector assertion that froze the buggy receive_id_type="thread_id" behavior with invariants: no-anchor sends degrade to chat_id, and anchored sends use ReplyMessage with reply_in_thread=True so media lands in the topic. - Add base.py regression tests that Feishu thread metadata carries a reply_to_message_id anchor (and falls back to the source message id) without leaking telegram-only keys. Relates to NousResearch#39526.
Related: part of the Feishu |
|
Thanks for tracing the invalid Problems
Suggested changes
Automated hermes-sweeper review. |
|
I reproduced this failure on Hermes v2026.7.30 / 0.19.1 in a real Lark topic group: the text reply stayed in the topic, but the requested native This branch is now merge-conflicted with |
Summary
Fixes #39526. In Feishu topic groups (话题群), media/file attachments sent by Hermes fail with
[99992402] field validation failedand are silently dropped (the user receives nothing — not the thread, not the main chat). Plain text responses work because they take theReplyMessagepath; media sends reach the adapter with no reply anchor and fall into aCreateMessagecall that usesreceive_id_type="thread_id".Root cause:
thread_idis not a validreceive_id_typefor Feishu'sPOST /im/v1/messages. Per the official docs the only accepted values areopen_id/user_id/union_id/email/chat_id. Sendingreceive_id_type="thread_id"is rejected with99992402.I confirmed this independently against the live API (same app credentials, isolated topic chat): a bare
POST /im/v1/messages?receive_id_type=thread_idwith plain text returns99992402, while the same payload withreceive_id_type=chat_idsucceeds — proving the failure is bound to the invalidreceive_id_type, not the message/file type. This is the same defective branch reached by #35576 (auto-resume → stalethread_id→99992402) via a different trigger.Changes
Two changes that fix the whole bug class (all media siblings: images, files, audio, video), not just the
.mdcase the reporter hit:gateway/platforms/base.py—_thread_metadata_for_source()now carries areply_to_message_idanchor for Feishu threads (falling back to the source message id). This lets media/file sends — which otherwise arrive at the adapter with no reply target — use the supportedReplyMessage+reply_in_thread=Truepath so the attachment lands inside the topic. Telegram/other-platform metadata is untouched (the anchor is added only on thefeishubranch, and Telegram already uses its owntelegram_reply_to_message_idkey).plugins/platforms/feishu/adapter.py—_send_raw_message()no longer emits the invalidreceive_id_type="thread_id". When no reply anchor is available it degrades tochat_iddelivery so the message still reaches the conversation instead of being dropped.Tests
receive_id_type=="thread_id"assertion with behavior invariants:chat_id(neverthread_id)ReplyMessagewithreply_in_thread=Truebase.pyregression tests: Feishu thread metadata carriesreply_to_message_id(and falls back to the source message id) without leaking Telegram-only keys.All updated logic verified with real imports against the changed modules (10/10 invariant checks pass).
python -m astparse + the patch tool's lint pass clean on all four files.Why this approach
The supported way to place a message inside a Feishu topic is
ReplyMessagewithreply_in_thread=trueanchored to a realom_message id — not athread_idreceive type that the API rejects. When an anchor genuinely isn't available,chat_iddelivery is an always-valid degrade (message reaches the conversation) and matches the existing fallback philosophy referenced in #35576.Closes #39526.