Skip to content

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

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

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions