RocketChat/Rocket.Chat · error · Meteor.Error
error-message-same-as-tmid
error-message-same-as-tmid
Error message
Cannot set tmid the same as the _id
What it means
Thrown by Rocket.Chat's updateMessage Meteor method when an edit payload sets tmid (the thread parent id) to the message's own _id. The server compares the stored message's _id with the incoming tmid and rejects a self-referencing thread reference, because a message cannot be its own thread parent. This is a request-validation guard, not a server fault.
Source
Thrown at apps/meteor/server/meteor-methods/messages/updateMessage.ts:43
Object.entries(message).forEach(([key, value]) => {
if (!allowedEditedFields.includes(key) && value !== originalMessage[key as keyof IMessage]) {
throw new Meteor.Error('error-invalid-update-key', `Cannot update the message ${key}`, {
method: 'updateMessage',
});
}
});
// 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',
});
}View on GitHub (pinned to b2c16d5842)
Solutions
- Remove tmid from the edit payload — the server preserves the message's existing threading; only send fields you intend to change (msg, attachments, customFields)
- If the goal is to reply inside a thread, send a new message with tmid set to the parent message's _id instead of editing
- Audit payload construction so tmid always references the parent message and never the edited message's own _id
Example fix
// before
Meteor.call('updateMessage', { ...msg, tmid: msg._id }); // self reference
// after
const { tmid, ...changes } = msg;
Meteor.call('updateMessage', { _id: msg._id, ...changes }); // no tmid on edit Defensive patterns
Strategy: validation
Validate before calling
const tmidIsValid = (payload: { _id: string; tmid?: string }): boolean =>
!payload.tmid || payload.tmid !== payload._id;
if (!tmidIsValid(editPayload)) {
throw new Error('refusing to submit: tmid equals _id');
} Type guard
const isSameTmidError = (e: unknown): e is Meteor.Error =>
typeof e === 'object' && e !== null && (e as { error?: string }).error === 'error-message-same-as-tmid'; Try / catch
try {
await Meteor.callAsync('updateMessage', payload);
} catch (e) {
if (isSameTmidError(e)) {
const { tmid, ...rest } = payload;
return Meteor.callAsync('updateMessage', rest); // retry once without tmid
}
throw e;
} Prevention
- Build edit payloads from explicit fields (_id, msg) instead of spreading fetched message objects
- Treat tmid as create-only: never set it on an edit
- Add a client-side assertion that tmid !== _id before every updateMessage call
When it happens
Trigger: Calling Meteor.call('updateMessage', message) with message.tmid truthy and equal to the _id of the message being edited. Typically the payload is built by spreading an existing message object and tmid gets assigned from the wrong variable (the message's own _id instead of its parent's _id).
Common situations: Custom clients or Apps-Engine code that reuses a fetched message object as the edit payload; thread UIs mixing up the current message id and the parent message id; bots or migration scripts replaying edits with reconstructed payloads.
Related errors
- error-message-change-to-thread
- 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/955add2c3fc3ba9e.
Report an issue: GitHub.