RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-message

error-invalid-message

Error message

Invalid Message

What it means

Thrown by chat.readThread when Messages.findOneById(tmid, { projection: { rid: 1 } }) returns no document or one without an rid - i.e., the supplied thread parent message (tmid) does not exist. Meteor.Error code 'error-invalid-message'. This runs only after the Threads_enabled check passes.

Source

Thrown at apps/meteor/server/api/v1/chat.ts:289

							type: 'boolean',
							enum: [true],
						},
					},
					required: ['success'],
					additionalProperties: false,
				}),
			},
		},
		async function action() {
			if (!settings.get<boolean>('Threads_enabled')) {
				throw new Meteor.Error('error-not-allowed', 'Threads Disabled');
			}

			const { tmid } = this.bodyParams;

			const thread = await Messages.findOneById(tmid, { projection: { rid: 1 } });
			if (!thread?.rid) {
				throw new Meteor.Error('error-invalid-message', 'Invalid Message');
			}

			const [user, room] = await Promise.all([
				Users.findOneById(this.userId),
				Rooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }),
			]);

			if (!room) {
				throw new Meteor.Error('error-room-does-not-exist', 'This room does not exist');
			}

			if (!user || !(await canAccessRoomAsync(room, user))) {
				throw new Meteor.Error('error-not-allowed', 'Not Allowed');
			}

			await callbacks.run('beforeReadMessages', room._id, user._id);
			await readThread({ user, room, tmid });

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Confirm tmid refers to the thread-root message (the parent), not a reply within the thread.
  2. Validate that the thread root still exists before marking read.
  3. Handle error-invalid-message by refreshing the thread list.

Example fix

// before
await fetch('/api/v1/chat.readThread', { method: 'POST', body: JSON.stringify({ tmid }) });

// after
const root = await fetchMessage(tmid);
if (!root || !root.rid) {
  refreshThreadList();
  return;
}
await fetch('/api/v1/chat.readThread', { method: 'POST', body: JSON.stringify({ tmid }) });
Defensive patterns

Strategy: validation

Validate before calling

async function threadRootExists(tmid) {
  const msg = await Messages.findOneById(tmid, { projection: { rid: 1 } });
  return Boolean(msg && msg.rid);
}

Type guard

function isValidThreadRoot(msg) {
  return Boolean(msg) && typeof msg.rid === 'string' && msg.rid.length > 0;
}

Try / catch

try {
  await api.readThread({ tmid });
} catch (e) {
  if (e.error === 'error-invalid-message') {
    refreshThreadList(); // tmid stale or wrong
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST chat.readThread with a tmid that is not a real message id, or that is a message with no rid (malformed/deleted).

Common situations: Stale tmid from a thread whose parent message was deleted; tmid typo; using a reply message id as tmid instead of the thread root.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/0c21c63e5b34771a. Report an issue: GitHub.