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
- Identify the vetoing app: check Admin → Apps for apps implementing IPreRoomUserLeave, and read error.message (it is the app's own text)
- Disable or reconfigure that app if leaving should be allowed
- For internal flows that must proceed regardless (like the ABAC rationale in the code), pass { skipAppPreEvents: true } in the options
- 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
- Audit installed apps for IPreRoomUserLeave handlers when users report they cannot leave rooms
- Pass { skipAppPreEvents: true } only for system flows that must not be vetoed (same rationale as the ABAC path)
- As an app developer, veto sparingly — blocking leave can trap users
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
- error-invalid-command
- error-invalid-command
- error-cannot-delete-app-user
- error-user-already-in-room
- Invalid command parameter provided, must be a string.
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/a9e498bfe7926a04.
Report an issue: GitHub.