RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-room
error-invalid-room
Error message
Invalid room
What it means
getThreadsList (deprecated in 9.0.0 in favor of GET /v1/chat.getThreadsList) throws error-invalid-room when its rid parameter is not a string — a manual typeof check placed before the room lookup. It is not a 'room not found' signal: a well-typed but unknown or inaccessible rid instead produces error-not-allowed further down (getThreadsList.ts:39-41). The error means the client sent undefined, null, a number, or an object where the room _id string was expected.
Source
Thrown at apps/meteor/server/meteor-methods/messages/getThreadsList.ts:33
getThreadsList(params: { rid: IRoom['_id']; limit?: number; skip?: number }): IMessage[];
}
}
Meteor.methods<ServerMethods>({
async getThreadsList({ rid, limit = 50, skip = 0 }) {
methodDeprecationLogger.method('getThreadsList', '9.0.0', '/v1/chat.getThreadsList');
if (limit > MAX_LIMIT) {
throw new Meteor.Error('error-not-allowed', `max limit: ${MAX_LIMIT}`, {
method: 'getThreadsList',
});
}
if (!Meteor.userId() || !settings.get('Threads_enabled')) {
throw new Meteor.Error('error-not-allowed', 'Threads Disabled', { method: 'getThreadsList' });
}
if (typeof rid !== 'string') {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getThreadsList' });
}
const user = await Meteor.userAsync();
const room = await Rooms.findOneById(rid);
if (!user || !room || !(await canAccessRoomAsync(room, user))) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed', { method: 'getThreadsList' });
}
return Messages.findThreadsByRoomId(room._id, skip, limit).toArray();
},
});
View on GitHub (pinned to 2a7de45707)
Solutions
- Pass the room's _id string; when you hold a room record, send room._id, never the record itself or room.rid.
- Skip the call until rid is a non-empty string — guard at the call site or bail out of the reactive effect early.
- Fix ordering: wait for route/subscription data to resolve before invoking the method.
- Migrate to GET /v1/chat.getThreadsList — the DDP method is deprecated for removal in 9.0.0.
Example fix
// before — `room` is the fetched room DOCUMENT (no rid field on it),
// so rid is undefined and the server throws error-invalid-room
await Meteor.callAsync('getThreadsList', { rid: room.rid, limit: 50 });
// after — pass the room's _id string
await Meteor.callAsync('getThreadsList', { rid: room._id, limit: 50 }); Defensive patterns
Strategy: validation
Validate before calling
const isRoomId = (value: unknown): value is IRoom['_id'] =>
typeof value === 'string' && value.length > 0;
// inside a reactive effect: skip until the route actually resolved a room id
if (!isRoomId(rid)) {
return;
}
const threads = Meteor.call('getThreadsList', { rid, limit: 50, skip: 0 }); Type guard
const isRoomId = (value: unknown): value is IRoom['_id'] => typeof value === 'string' && value.length > 0;
Try / catch
try {
const threads = Meteor.call('getThreadsList', { rid, limit: 50 });
} catch (error) {
if (error instanceof Meteor.Error && error.error === 'error-invalid-room') {
// programmer error: rid never reached the call as a string — fix the caller, do NOT retry.
// unknown-but-valid ids surface as error-not-allowed instead
}
throw error;
} Prevention
- Always send room._id, never the room record or a differently named field.
- Skip method calls while route/subscription data is unresolved rather than calling with undefined.
- Type the params object at the call site ({ rid: string }) so the compiler catches undefined/null/object inputs.
- Do not retry this error — it is deterministic bad input, not a transient failure.
When it happens
Trigger: Meteor.call('getThreadsList', { rid, limit, skip }) with rid undefined (e.g., destructured from a route parameter that is not resolved yet), null, a numeric id, or a whole room document instead of room._id. The check fires only after the limit check (default 50, max 100) and the Threads_enabled/authentication checks pass.
Common situations: Router params not yet resolved when a component's effect first runs (rid undefined on first render), state initialized to null, or passing subscription data (the room record) rather than its _id. Common when porting REST integrations to DDP: path-typed string ids in REST become whatever JavaScript value is at hand in a method payload.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@2a7de45707 (2026-08-21).
Data as JSON: /api/errors/20914a3d29f4f5b0.
Report an issue: GitHub.