RocketChat/Rocket.Chat · error · Meteor.Error
error-not-allowed
error-not-allowed
Error message
Not allowed
What it means
getRoomById maps a missing room to 'error-not-allowed' instead of a dedicated not-found code: Rooms.findOneById(rid) returned null, so no room document exists with that _id. Because the same code is reused at line 34 for real access denial, this error alone cannot distinguish 'no such room' from 'no access'.
Source
Thrown at apps/meteor/server/meteor-methods/rooms/getRoomById.ts:29
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
getRoomById(rid: IRoom['_id']): IRoom;
}
}
Meteor.methods<ServerMethods>({
async getRoomById(rid) {
check(rid, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'getRoomNameById',
});
}
const room = await Rooms.findOneById(rid);
if (room == null) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'getRoomNameById',
});
}
if (!(await canAccessRoomAsync(room, (await Meteor.userAsync()) as IUser))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'getRoomById',
});
}
return room;
},
});
DDPRateLimiter.addRule(
{
type: 'method',
name: 'getRoomById',
userId() {
return true;View on GitHub (pinned to b2c16d5842)
Solutions
- Re-fetch the rid from a live source (rooms subscription or the /channel/<name> routing) instead of a cached value
- Confirm the room exists on the server: db.rooms.findOne({_id: rid})
- Handle 'error-not-allowed' as either not-found or no-access: refresh the room list and drop the stale reference
- Prefer GET /api/v1/rooms.info?roomId=... which returns a distinguishable not-found response
Example fix
// before - rid may be stale after the room was deleted
const room = await Meteor.callAsync('getRoomById', rid);
// after - treat failure as invalid reference and resync
try {
const room = await Meteor.callAsync('getRoomById', rid);
} catch (e) {
if (e?.error === 'error-not-allowed') {
await refreshRoomList(); // drop stale rid, reload from server truth
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify shape and liveness of the rid before the call
if (typeof rid !== 'string' || rid.length !== 17) throw new Error('malformed room id');
const known = RoomManager.getOpenedRoomByRid(rid); // client room cache
if (!known) throw new Error('unknown room'); Try / catch
try {
const room = await Meteor.callAsync('getRoomById', rid);
} catch (e) {
if (e instanceof Meteor.Error && e.error === 'error-not-allowed') {
// ambiguous: room missing OR access denied - resync room list and drop the rid
await refreshRooms();
}
} Prevention
- Derive rids from live subscriptions rather than persisted caches
- Remember 'error-not-allowed' from this method covers not-found too
- Prefer /v1/rooms.info when you must distinguish 404 from 403
When it happens
Trigger: Meteor.call('getRoomById', rid) with a nonexistent, already-deleted, or mistyped room id. Since check(rid, String) passes for any string, this path specifically means a well-formed id with no matching Rooms document.
Common situations: Stale rid persisted in localStorage or a URL from a room that was later deleted; race where the rooms subscription updates after a deletion but UI code still holds the old rid; passing a subscription id or message id where a room id was expected.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/16ee1c69bff85c2f.
Report an issue: GitHub.