RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-message

error-invalid-message

Error message

Invalid message

What it means

unfollowMessage loads the target with Messages.findOneById(mid); a null result throws error-invalid-message. The id is unknown: the message was deleted, never existed, or came from a different workspace. There is no authorization involved yet at this point.

Source

Thrown at apps/meteor/server/meteor-methods/messages/unfollowMessage.ts:29

import { unfollow } from '../../lib/messaging/threads/functions';
import { notifyOnMessageChange } from '../../lib/notifyListener';
import { settings } from '../../settings';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		unfollowMessage(message: { mid: IMessage['_id'] }): false | undefined;
	}
}

export const unfollowMessage = async (user: IUser, { mid }: { mid: IMessage['_id'] }): Promise<false | undefined> => {
	if (mid && !settings.get('Threads_enabled')) {
		throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
	}

	const message = await Messages.findOneById(mid);
	if (!message) {
		throw new Meteor.Error('error-invalid-message', 'Invalid message', {
			method: 'unfollowMessage',
		});
	}

	if (!(await canAccessRoomIdAsync(message.rid, user._id))) {
		throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
	}

	const id = message.tmid || message._id;

	const unfollowResult = await unfollow({ rid: message.rid, tmid: id, uid: user._id });

	void notifyOnMessageChange({
		id,
	});

	const isFollowed = false;
	await Apps.self?.triggerEvent(AppEvents.IPostMessageFollowed, message, user, isFollowed);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Treat as stale state: clear the local follow indicator, do not retry
  2. Verify the message/thread still exists before offering the unfollow action
  3. Refresh thread data after deletions or retention purges
Defensive patterns

Strategy: try-catch

Try / catch

try {
	await Meteor.callAsync('unfollowMessage', { mid });
} catch (e: any) {
	if (e?.error === 'error-invalid-message') {
		// message gone: clear local follow state, treat as success (idempotent)
		markNotFollowed(mid);
		return;
	}
	throw e;
}

Prevention

When it happens

Trigger: Message deleted or purged by retention while its thread was still open in the client; passing a malformed or wrong mid; ids from a pre-restore database snapshot.

Common situations: Retention jobs purging old thread parents; moderator deletes while users view the thread; cache pointing at messages that no longer exist.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/daf05f6cd9bb307b. Report an issue: GitHub.