RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
Not Allowed
What it means
Thrown by POST rooms.mail when the calling user can't be loaded or fails canAccessRoomAsync for the target room. This is the access-control gate after the room is confirmed to exist; it blocks users who exist in the system but have no access to the channel (e.g. a private channel they're not in).
Source
Thrown at apps/meteor/server/api/v1/rooms.ts:1023
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { rid, type } = this.bodyParams;
if (!(await hasPermissionAsync(this.user, 'mail-messages', rid))) {
throw new Meteor.Error('error-action-not-allowed', 'Mailing is not allowed');
}
const room = await Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-invalid-room');
}
const user = await Users.findOneById(this.userId);
if (!user || !(await canAccessRoomAsync(room, user))) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
}
if (type === 'file') {
const { dateFrom, dateTo } = this.bodyParams;
const { format } = this.bodyParams;
const convertedDateFrom = dateFrom ? new Date(dateFrom) : new Date(0);
const convertedDateTo = dateTo ? new Date(dateTo) : new Date();
convertedDateTo.setDate(convertedDateTo.getDate() + 1);
if (convertedDateFrom > convertedDateTo) {
throw new Meteor.Error('error-invalid-dates', 'From date cannot be after To date');
}
void dataExport.sendFile(
{
rid,
format,View on GitHub (pinned to f9d3ec372b)
Solutions
- Only offer the mail action on rooms the user can access (check via rooms.canAccess or by only listing joined rooms).
- Re-fetch /me on session resume to confirm the account is still valid.
- For private channels, ensure membership before exposing the action.
Example fix
// before
await rest.post('/api/v1/rooms.mail', { rid, type: 'file' });
// after
const accessible = await rest.get(`/api/v1/rooms.info?roomId=${rid}`);
if (!accessible.room || !accessible.room._accessible) {
notify('You do not have access to this room.');
return;
}
await rest.post('/api/v1/rooms.mail', { rid, type: 'file' }); Defensive patterns
Strategy: validation
Validate before calling
const accessible = await canAccessRoom(rid, userId);
if (!accessible) throw new Error('User cannot access this room'); Type guard
function canAccess(room: { _accessible?: boolean } | undefined): boolean {
return !!room?._accessible;
} Try / catch
try {
await rest.post('/api/v1/rooms.mail', { rid, type });
} catch (e) {
if (isMeteorError(e, 'error-not-allowed')) {
notify('You do not have access to this room.');
} else throw e;
} Prevention
- Only show mail/export for joined rooms.
- Re-validate access after long sessions.
- For private channels, gate the UI on membership.
When it happens
Trigger: POST /api/v1/rooms.mail where the user was deleted between auth and lookup, or where the room is private/DM and the user is not a member.
Common situations: Trying to mail/export a private channel the user isn't part of; cross-workroom access attempts; user account disabled mid-session.
Related errors
- error-action-not-allowed
- Room not found
- error-not-authorized
- invalid-calendar-event
- error-room-param-not-provided
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/245accf3c37f3c66.
Report an issue: GitHub.