RocketChat/Rocket.Chat · error · Meteor.Error
error-message-change-to-thread
error-message-change-to-thread
Error message
Cannot update message to a thread
What it means
Thrown by Rocket.Chat's updateMessage when the stored message has no tmid (a top-level channel message) but the edit payload includes one. Messages cannot be moved into a thread by editing; thread membership is fixed at creation time. The server enforces this by comparing originalMessage.tmid against the incoming message.tmid.
Source
Thrown at apps/meteor/server/meteor-methods/messages/updateMessage.ts:49
}
});
// IF the message has custom fields, always update
// Ideally, we'll compare the custom fields to check for change, but since we don't know the shape of
// custom fields, as it's user defined, we're gonna update
const msgText = originalMessage?.attachments?.[0]?.description ?? originalMessage.msg;
if (msgText === message.msg && !previewUrls && !message.customFields) {
return;
}
if (!!message.tmid && originalMessage._id === message.tmid) {
throw new Meteor.Error('error-message-same-as-tmid', 'Cannot set tmid the same as the _id', {
method: 'updateMessage',
});
}
if (!originalMessage.tmid && !!message.tmid) {
throw new Meteor.Error('error-message-change-to-thread', 'Cannot update message to a thread', { method: 'updateMessage' });
}
const _hasPermission = await hasPermissionAsync(uid, 'edit-message', message.rid);
const editAllowed = settings.get('Message_AllowEditing');
const editOwn = originalMessage.u && originalMessage.u._id === uid;
if (!_hasPermission && (!editAllowed || !editOwn)) {
throw new Meteor.Error('error-action-not-allowed', 'Message editing not allowed', {
method: 'updateMessage',
action: 'Message_editing',
});
}
const blockEditInMinutes = settings.get('Message_AllowEditing_BlockEditInMinutes');
const bypassBlockTimeLimit = await hasPermissionAsync(uid, 'bypass-time-limit-edit-and-delete', message.rid);
if (!bypassBlockTimeLimit && Match.test(blockEditInMinutes, Number) && blockEditInMinutes !== 0) {
let currentTsDiff = 0;View on GitHub (pinned to b2c16d5842)
Solutions
- Strip tmid from the edit payload; send only the fields being changed
- To get the content into a thread: send a new message with tmid of the desired parent, then delete the original
- Guard payload construction: include tmid only when editing a message that already has one
Example fix
// before
Meteor.call('updateMessage', { _id: msg._id, msg: text, tmid: parent._id }); // msg is not threaded
// after
Meteor.call('updateMessage', { _id: msg._id, msg: text });
// to thread it instead: post a new reply
// Meteor.call('sendMessage', { rid, msg: text, tmid: parent._id }) Defensive patterns
Strategy: validation
Validate before calling
const mayIncludeTmid = (original: { tmid?: string }, payload: { tmid?: string }): boolean =>
!payload.tmid || !!original.tmid;
if (!mayIncludeTmid(originalMessage, payload)) {
delete payload.tmid; // a non-thread message cannot be edited into a thread
} Type guard
const isChangeToThreadError = (e: unknown): e is Meteor.Error =>
typeof e === 'object' && e !== null && (e as { error?: string }).error === 'error-message-change-to-thread'; Try / catch
try {
await Meteor.callAsync('updateMessage', payload);
} catch (e) {
if (isChangeToThreadError(e)) {
const { tmid, ...rest } = payload;
return Meteor.callAsync('updateMessage', rest);
}
throw e;
} Prevention
- Type edit payloads as Omit<Message, 'tmid'> so the field is excluded at compile time
- Never include tmid in edit payloads; thread membership is immutable
- When content belongs in a thread, post a new threaded message instead of editing
When it happens
Trigger: Calling Meteor.call('updateMessage', { ...msg, tmid }) where the stored message has no tmid — e.g. an edit form pre-filled with thread context, or payload builders that unconditionally attach tmid to every edit call.
Common situations: An 'edit in thread' UI applied to a message originally posted directly to the channel; bots reusing thread-reply payload shapes for all edits; attempts to retro-organize old messages into threads via bulk edit.
Related errors
- error-message-same-as-tmid
- error-action-not-allowed
- error-message-editing-blocked
- error-invalid-payload
- error-invalid-room
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/bd236c8b391f205c.
Report an issue: GitHub.