Skip to content

fix(feishu): ensure replies stay inside topic threads in group chats#16018

Closed
highland0971 wants to merge 1 commit into
NousResearch:mainfrom
highland0971:fix/feishu-topic-reply-threading
Closed

fix(feishu): ensure replies stay inside topic threads in group chats#16018
highland0971 wants to merge 1 commit into
NousResearch:mainfrom
highland0971:fix/feishu-topic-reply-threading

Conversation

@highland0971

Copy link
Copy Markdown

Problem

When a user sends a message inside a Feishu group topic thread, the bot's reply spawns a new topic instead of staying inside the existing thread. This affects all message types: final responses, tool progress messages, and streaming partial responses.

Root Cause

Three gaps in the Feishu adapter's topic threading logic:

  1. Inbound normalization (feishu.py): When a message is a reply within a topic (non-root message), message.thread_id is empty — only message.root_id contains the topic ID. The code only read thread_id, missing the fallback to root_id.

  2. Outbound send (_send_raw_message): Feishu uses two ID prefixes:

    • om_ → message ID (valid as reply_to for im.v1.message.reply)
    • omt_ → topic/thread ID (not valid as reply_to — triggers API error 99992354)

    When reply_to was empty and only thread_id (an omt_ ID) was available, the code fell through to im.v1.message.create which doesn't place messages inside topics.

  3. Gateway runner (run.py): Metadata carried thread_id but not reply_to_message_id (the original user message ID), so downstream consumers (stream, progress, status callbacks) couldn't use im.v1.message.reply to stay inside topics.

Fix (3 changes)

gateway/platforms/feishu.py

  • _normalize_inbound_message: Add root_id → thread_id fallback
  • _send_raw_message:
    • om_ thread_id (message ID) → use as reply_to directly
    • omt_ thread_id (topic ID) → use reply_to_message_id from metadata as the reply target
    • Fallback: pass root_id to im.v1.message.create so new messages land inside the topic
  • _build_create_message_body: Accept optional root_id parameter
  • Added TOPIC_DEBUG / SEND_DEBUG logging for future troubleshooting

gateway/run.py

  • Initial send: When source.thread_id starts with omt_, add reply_to_message_id = original event_message_id to metadata
  • Progress messages: Same treatment — carry reply_to_message_id for Feishu topics
  • Status callbacks: Same
  • Stream consumer: Same

Testing

Verified in production (Feishu group chat with topics enabled). Four scenarios tested:

  1. ✅ Reply from topic root message → stays in topic
  2. ✅ Reply from non-root message inside topic → stays in topic
  3. ✅ Tool progress messages → appear inside topic
  4. ✅ Streaming partial responses → appear inside topic

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter comp/gateway Gateway runner, session dispatch, delivery labels Apr 26, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Multiple open PRs address the same Feishu topic threading issue: #8656, #11433, #13077. This PR appears to be the most comprehensive fix (covers inbound normalization, outbound send, and gateway runner). Consider closing the earlier PRs if this one is adopted. Related issue: #6969.

When a user sends a message inside a Feishu group topic thread,
the bot's reply was spawning a new topic instead of staying in
the existing thread. This happened because:

1. Inbound: thread_id wasn't being extracted from root_id when
   the message came from a non-root message inside a topic.
2. Outbound: _send_raw_message had no fallback logic to convert
   omt_ topic IDs into valid reply targets.
3. Gateway runner: metadata didn't carry reply_to_message_id
   for Feishu topics, so intermediate/progress messages couldn't
   use im.v1.message.reply with reply_in_thread=True.

Changes:
- feishu.py _normalize_inbound_message: fallback thread_id from
  root_id; add TOPIC_DEBUG logging
- feishu.py _send_raw_message: detect omt_ prefix, use
  reply_to_message_id from metadata; pass root_id when creating
  messages to place them inside topics
- feishu.py _build_create_message_body: accept optional root_id
- gateway/run.py: carry reply_to_message_id in metadata for all
  Feishu topic paths (initial, progress, status, stream consumer)

Closes: user-reported bug where bot replies always created new
topic threads instead of staying within the existing one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/feishu Feishu / Lark adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants