Skip to content

[Bug] Feishu: Bot replies create new topics instead of replying within existing topic #66631

@Xinyu-101

Description

@Xinyu-101

Fix: Feishu topic replies create new threads instead of replying within existing topic

Problem Description

When a bot replies to messages in Feishu topic groups, it creates new sub-threads instead of replying within the original topic thread. This happens specifically when users send new messages directly in a topic (not replying to other messages).

Root Cause

Feishu API Behavior

Feishu message events return different fields based on user action:

Scenario A: User sends new message in topic

{
  "message_id": "om_abc123",
  "thread_id": "omt_xyz789",   // Topic ID
  "root_id": null,              // ❌ NULL - not a reply
  "parent_id": null             // ❌ NULL
}

Scenario B: User replies to a message in topic

{
  "message_id": "om_new456",
  "thread_id": "omt_xyz789",
  "root_id": "om_root001",      // ✅ Has value
  "parent_id": "om_parent002"   // ✅ Has value
}

According to Feishu documentation:

"root_id: Root message ID, only returned in reply message scenarios"

Current OpenClaw Logic

const replyTargetMessageId = ctx.rootId ?? ctx.messageId;
// When rootId is null, falls back to current messageId

await client.im.message.reply({
    path: { message_id: replyTargetMessageId },  // Current message!
    data: { reply_in_thread: true }
});

// Result: Creates new thread rooted at current message ❌

Solution

When in a topic but root_id is not provided by Feishu, actively fetch the topic's root message ID via API:

// Add helper function
async function fetchThreadRootMessageId(
    client: FeishuClient, 
    threadId: string, 
    log: (msg: string) => void
): Promise<string | null> {
    try {
        log(`[thread-fix] Fetching root message for thread ${threadId}`);
        const res = await client.im.message.list({
            params: {
                container_id_type: "thread",
                container_id: threadId,
                page_size: 1,
                sort_type: "ByCreateTimeAsc"  // Earliest message (root)
            }
        });
        
        if (res.code === 0 && res.data?.items?.length > 0) {
            const rootMsgId = res.data.items[0].message_id;
            log(`[thread-fix] Found root message: ${rootMsgId}`);
            return rootMsgId;
        } else {
            log(`[thread-fix] Failed: code=${res.code}, items=${res.data?.items?.length || 0}`);
        }
    } catch (err) {
        log(`[thread-fix] Error: ${err.message}`);
    }
    return null;
}

// In message processing logic (before determining replyTargetMessageId):
if ((isTopicSession || configReplyInThread) && ctx.threadId && !ctx.rootId && !ctx.parentId) {
    const client = createFeishuClient(account);
    ctx.rootId = await fetchThreadRootMessageId(client, ctx.threadId, log);
}

const replyTargetMessageId = isTopicSession || configReplyInThread 
    ? ctx.rootId ?? ctx.parentId ?? ctx.messageId 
    : ctx.messageId;

Files to Modify

Based on the compiled output, the source file should be:

  • extensions/feishu/src/monitor.ts or similar
  • Look for the function handling message reply logic
  • Two locations need modification (error handling + normal reply)

Search for:

const replyTargetMessageId = ... ? ctx.rootId ?? ctx.messageId : ctx.messageId;

Locations in compiled code (for reference):

  • dist/monitor-*.js line ~1780 (error handling)
  • dist/monitor-*.js line ~2005 (normal reply)

Testing

Before Fix

  • User sends message in topic → Bot creates new thread ❌
  • First reply ("Acknowledged") → In topic ✅ (timing-dependent)
  • Second reply (final result) → New thread ❌

After Fix

  • User sends message in topic
  • Bot fetches root message via API
  • Both replies use root message ID → All in same topic ✅

Test Results

Tested on OpenClaw 2026.4.9:

  • Topic ID: omt_1abbdddc1bce9ce6
  • Root message ID fetched: om_x100b52d11f9e50b8b4ab28b8d7f034f
  • Result: ✅ Both bot replies stayed in the original topic

Debug log:

[thread-fix] Fetching root message for thread omt_1abbdddc1bce9ce6
[thread-fix] Found root message: om_x100b52d11f9e50b8b4ab28b8d7f034f
replyTarget resolved - rootId=om_x100b52d11f9e50b8b4ab28b8d7f034f, final=om_x100b52d11f9e50b8b4ab28b8d7f034f

Related Issues

Configuration Required

Users must enable:

{
  "channels": {
    "feishu": {
      "replyInThread": "enabled",
      "groupSessionScope": "group_topic"
    }
  }
}

Performance Impact

  • Adds one API call per topic message (when root_id is not provided by Feishu)
  • Typical latency: ~400ms (observed in testing)
  • Could be optimized with caching (thread_id → root message_id mapping)

Potential Optimization

Add LRU cache for thread_id → root message_id mapping:

const threadRootCache = new LRUCache<string, string>({ max: 100, ttl: 3600000 });

if (cached = threadRootCache.get(ctx.threadId)) {
    ctx.rootId = cached;
} else {
    ctx.rootId = await fetchThreadRootMessageId(...);
    threadRootCache.set(ctx.threadId, ctx.rootId);
}

Tested by: Justin Wang
Date: 2026-04-14
Version: OpenClaw 2026.4.9

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions