{"record":{"id":"20914a3d29f4f5b0","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-20914a","errorCode":"error-invalid-room","errorMessage":"Invalid room","messagePattern":"Invalid room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/getThreadsList.ts","lineNumber":33,"sourceCode":"\t\tgetThreadsList(params: { rid: IRoom['_id']; limit?: number; skip?: number }): IMessage[];\n\t}\n}\n\nMeteor.methods<ServerMethods>({\n\tasync getThreadsList({ rid, limit = 50, skip = 0 }) {\n\t\tmethodDeprecationLogger.method('getThreadsList', '9.0.0', '/v1/chat.getThreadsList');\n\t\tif (limit > MAX_LIMIT) {\n\t\t\tthrow new Meteor.Error('error-not-allowed', `max limit: ${MAX_LIMIT}`, {\n\t\t\t\tmethod: 'getThreadsList',\n\t\t\t});\n\t\t}\n\n\t\tif (!Meteor.userId() || !settings.get('Threads_enabled')) {\n\t\t\tthrow new Meteor.Error('error-not-allowed', 'Threads Disabled', { method: 'getThreadsList' });\n\t\t}\n\n\t\tif (typeof rid !== 'string') {\n\t\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getThreadsList' });\n\t\t}\n\n\t\tconst user = await Meteor.userAsync();\n\t\tconst room = await Rooms.findOneById(rid);\n\n\t\tif (!user || !room || !(await canAccessRoomAsync(room, user))) {\n\t\t\tthrow new Meteor.Error('error-not-allowed', 'Not Allowed', { method: 'getThreadsList' });\n\t\t}\n\n\t\treturn Messages.findThreadsByRoomId(room._id, skip, limit).toArray();\n\t},\n});\n","sourceCodeStart":15,"sourceCodeEnd":46,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2a7de457074cbb4d4373fbd9a4e5bea292c9c764/apps/meteor/server/meteor-methods/messages/getThreadsList.ts#L15-L46","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — `room` is the fetched room DOCUMENT (no rid field on it),\n// so rid is undefined and the server throws error-invalid-room\nawait Meteor.callAsync('getThreadsList', { rid: room.rid, limit: 50 });\n\n// after — pass the room's _id string\nawait Meteor.callAsync('getThreadsList', { rid: room._id, limit: 50 });","handlingStrategy":"validation","validationCode":"const isRoomId = (value: unknown): value is IRoom['_id'] =>\n  typeof value === 'string' && value.length > 0;\n\n// inside a reactive effect: skip until the route actually resolved a room id\nif (!isRoomId(rid)) {\n  return;\n}\nconst threads = Meteor.call('getThreadsList', { rid, limit: 50, skip: 0 });","typeGuard":"const isRoomId = (value: unknown): value is IRoom['_id'] =>\n  typeof value === 'string' && value.length > 0;","tryCatchPattern":"try {\n  const threads = Meteor.call('getThreadsList', { rid, limit: 50 });\n} catch (error) {\n  if (error instanceof Meteor.Error && error.error === 'error-invalid-room') {\n    // programmer error: rid never reached the call as a string — fix the caller, do NOT retry.\n    // unknown-but-valid ids surface as error-not-allowed instead\n  }\n  throw error;\n}","preventionTips":["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."],"tags":["meteor-methods","ddp","threads","argument-validation","type-error"],"backgroundTag":"meteor-invalid-method-argument","analyzedSha":"2a7de457074cbb4d4373fbd9a4e5bea292c9c764","analyzedAt":"2026-08-21T15:01:34.830Z","contentChangedAt":"2026-08-21T15:01:34.830Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}