RocketChat/Rocket.Chat · error · Error

Invalid message id

Error message

Invalid message id

What it means

Thrown by AppMessageBridge.delete when message.id is falsy. Deletion requires the target message id; without it the bridge cannot call deleteMessage, so it rejects the call immediately. It guards against undefined/id-less message objects reaching the delete path.

Source

Thrown at apps/meteor/app/apps/server/bridges/messages.ts:62

			throw new Error('Invalid editor assigned to the message for the update.');
		}

		// #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed.
		const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message, true);
		const editor = await Users.findOneById(message.editor.id);

		if (!editor) {
			throw new Error('Invalid editor assigned to the message for the update.');
		}

		await updateMessage(msg as IMessage, editor);
	}

	protected async delete(message: IAppsMessage, user: IAppsUser, appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is deleting a message.`);

		if (!message.id) {
			throw new Error('Invalid message id');
		}

		const convertedMsg = await this.orch.getConverters()?.get('messages').convertAppMessage(message);
		const convertedUser = (await Users.findOneById(user.id)) || this.orch.getConverters()?.get('users').convertToRocketChat(user);

		await deleteMessage(convertedMsg as IMessage, convertedUser);
	}

	protected async notifyUser(user: IAppsUser, message: IAppsMessage, appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is notifying a user.`);

		const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message);

		if (!msg) {
			return;
		}

		void api.broadcast('notify.ephemeralMessage', user.id, msg.rid, {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Ensure message.id is set to an existing message _id before calling deleteMessage.
  2. Fetch the message first via the read accessor and operate on the returned object.
  3. Add an assertion (if (!message.id) throw) in the App with a descriptive message.
  4. Distinguish delete-by-id from delete-by-object at the call site.

Example fix

// before
await app.getModify().deleteMessage({ msg: 'to delete' });

// after
if (!message.id) {
  throw new Error('Cannot delete a message without an id');
}
await app.getModify().deleteMessage(message);
Defensive patterns

Strategy: validation

Validate before calling

function assertMessageId(message: IAppsMessage): asserts message is IAppsMessage & { id: string } {
  if (!message.id) {
    throw new Error('Cannot delete message: message.id is required');
  }
}
assertMessageId(message);
await app.getModify().deleteMessage(message);

Type guard

const hasMessageId = (m: IAppsMessage): m is IAppsMessage & { id: string } =>
  typeof m.id === 'string' && m.id.length > 0;

Try / catch

try {
  await app.getModify().deleteMessage(message);
} catch (e) {
  if ((e as Error).message === 'Invalid message id') {
    // fetch the stored message and operate on its id
  }
  throw e;
}

Prevention

When it happens

Trigger: An App calls the message modifier's delete (e.g. app.getModify().deleteMessage(message)) with a message whose id is undefined, null, or empty string.

Common situations: App builds a partial message object for deletion and forgets id; App forwards a message that lost id through serialization; App confuses a constructed message (pre-send, no id) with a stored one; refactoring dropped the id field.

Related errors


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