Skip to content

[Bug]: replyToMode "off" ignored when incoming message has root_id — causes Invalid RootId delivery failures #52350

@oldmanstillcan

Description

@oldmanstillcan

Environment:

  • OpenClaw version: 2026.2.21 (@openclaw/mattermost plugin)
  • Install method: local (npm global install)
  • OS: Ubuntu 24.04 LTS
  • Model setup: ChatGPT OAuth (OpenClaw native gateway)

Description:
Setting replyToMode: \"off\" in the Mattermost channel config does not prevent thread replies. When an incoming message has a root_id (sent as a reply inside a Mattermost thread), the gateway extracts that root_id and passes it through to the outgoing POST request — ignoring the mode setting entirely. This causes the Mattermost API to reject the reply and the message is never delivered.

Steps to Reproduce:

  1. Configure a Mattermost channel with replyToMode: \"off\"
  2. Send a message to the bot as a reply inside an existing Mattermost thread (message has a non-empty root_id)
  3. Bot generates a response but delivery fails

Expected Behavior:
replyToMode: \"off\" posts all replies as top-level channel messages unconditionally — regardless of whether the incoming message was part of a thread.

Actual Behavior:
Bot fails to deliver reply. Mattermost returns 400 Bad Request: Invalid RootId parameter.

Logs / Evidence:

[mattermost] final reply failed: Error: Mattermost API 400 Bad Request: Invalid RootId parameter.

Root Cause (identified):
In resolveMattermostEffectiveReplyToId (monitor.ts), the replyToMode check is short-circuited when threadRootId is set — it returns the thread root immediately without ever checking the mode:

// current — replyToMode bypassed for threaded messages
const threadRootId = params.threadRootId?.trim();
if (threadRootId) {
  return threadRootId; // replyToMode never reached
}
return params.replyToMode === "all" || params.replyToMode === "first" ? postId : undefined;

One-line fix:

if (threadRootId && params.replyToMode !== "off") {
  return threadRootId;
}

This makes replyToMode: \"off\" unconditionally suppress thread replies as documented/expected.

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