RocketChat/Rocket.Chat · error · Meteor.Error

error-app-prevented

error-app-prevented

Error message

error.message

What it means

Thrown by removeUserFromRoom when an app's IPreRoomUserLeave event handler vetoes the removal (removeUserFromRoom.ts:106). Apps.self.triggerEvent(AppEvents.IPreRoomUserLeave, room, user, options?.byUser) threw an AppsEngineException, which the server rewraps as Meteor.Error('error-app-prevented', error.message) — the message is whatever the app supplied. Bypassed entirely when options.skipAppPreEvents is set (the ABAC path deliberately skips it so apps cannot trap users in rooms).

Source

Thrown at apps/meteor/server/lib/rooms/removeUserFromRoom.ts:106

 * that should propagate normally to federation and other subscribers.
 */
export const removeUserFromRoom = async function (
	rid: string,
	user: IUser,
	options?: { byUser?: IUser; skipAppPreEvents?: boolean; customSystemMessage?: MessageTypesValues },
): Promise<void> {
	const room = await Rooms.findOneById(rid);
	if (!room) {
		return;
	}

	// Rationale: for an abac room, we don't want apps to be able to prevent a user from leaving
	if (!options?.skipAppPreEvents) {
		try {
			await Apps.self?.triggerEvent(AppEvents.IPreRoomUserLeave, room, user, options?.byUser);
		} catch (error: any) {
			if (error.name === AppsEngineException.name) {
				throw new Meteor.Error('error-app-prevented', error.message);
			}

			throw error;
		}
	}

	await Room.beforeLeave(room);

	await performUserRemoval(room, user, options);

	await afterLeaveRoomCallback.run({ user, kicker: options?.byUser }, room);

	await Apps.self?.triggerEvent(AppEvents.IPostRoomUserLeave, room, user, options?.byUser);
};

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Identify the vetoing app: check Admin → Apps for apps implementing IPreRoomUserLeave, and read error.message (it is the app's own text)
  2. Disable or reconfigure that app if leaving should be allowed
  3. For internal flows that must proceed regardless (like the ABAC rationale in the code), pass { skipAppPreEvents: true } in the options
  4. If you own the app, restrict the veto to genuinely required cases instead of blocking every leave

Example fix

// before
await removeUserFromRoom(rid, user);

// after (internal flow that must not be vetoed)
await removeUserFromRoom(rid, user, { skipAppPreEvents: true });
Defensive patterns

Strategy: try-catch

Try / catch

try {
	await removeUserFromRoom(rid, user, { byUser });
} catch (e) {
	if (e instanceof Meteor.Error && e.error === 'error-app-prevented') {
		// e.reason is the app's veto message: surface it and point the admin to the app
		throw new Meteor.Error('error-app-prevented', `An app prevented leaving: ${e.reason}`);
	}
	throw e;
}

Prevention

When it happens

Trigger: An installed Apps-Engine app implements preRoomUserLeave and calls something like throw new AppsEngineException('Cannot leave during onboarding'); then any leaveRoom/kick/removeUser API call on that room throws. Only fires when skipAppPreEvents is not passed.

Common situations: Third-party onboarding/gatekeeper apps blocking departures; app updated to enforce membership policies; admin unaware an app vetoes 'leave' actions while debugging why users cannot leave a channel.

Related errors


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