{"record":{"id":"ba6426a9b5ccbd29","repo":"RocketChat/Rocket.Chat","slug":"invalid-param","errorCode":"invalid-param","errorMessage":"invalid-param","messagePattern":"invalid-param","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/ee/server/api/v1/omnichannel/rooms.ts","lineNumber":61,"sourceCode":"\n\t\t\treturn API.v1.success();\n\t\t},\n\t},\n);\n\nAPI.v1.addRoute(\n\t'livechat/room.resumeOnHold',\n\t{\n\t\tauthRequired: true,\n\t\tpermissionsRequired: ['view-l-room'],\n\t\tvalidateParams: isLivechatRoomResumeOnHoldProps,\n\t\tlicense: ['livechat-enterprise'],\n\t},\n\t{\n\t\tasync post() {\n\t\t\tconst { roomId } = this.bodyParams;\n\t\t\tif (!roomId || roomId.trim() === '') {\n\t\t\t\tthrow new Error('invalid-param');\n\t\t\t}\n\n\t\t\ttype Room = Pick<IOmnichannelRoom, '_id' | 't' | 'open' | 'onHold' | 'servedBy' | 'u' | 'lastMessage'>;\n\n\t\t\tconst room = await LivechatRooms.findOneById<Room>(roomId, {\n\t\t\t\tprojection: { t: 1, open: 1, onHold: 1, servedBy: 1 },\n\t\t\t});\n\t\t\tif (!room) {\n\t\t\t\tthrow new Error('error-invalid-room');\n\t\t\t}\n\n\t\t\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId, { projection: { _id: 1 } });\n\t\t\tif (!subscription && !(await hasPermissionAsync(this.user, 'on-hold-others-livechat-room'))) {\n\t\t\t\tthrow new Error('Not_authorized');\n\t\t\t}\n\n\t\t\tconst { name, username, _id: userId } = this.user;\n\t\t\tconst onHoldBy = { _id: userId, username, name };","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/api/v1/omnichannel/rooms.ts#L43-L79","documentation":"POST livechat/room.resumeOnHold manually checks that body roomId is present and not only whitespace, throwing invalid-param before the room lookup. This backs up the isLivechatRoomResumeOnHoldProps schema, which permits the field to be missing or blank.","triggerScenarios":"POST /api/v1/livechat/room.resumeOnHold with {}, {\"roomId\": \"\"} or {\"roomId\": \"   \"}.","commonSituations":"Client sending the value under a different key (rid, room_id) because other routes take rid in the URL; an empty form field submitted; JSON bodies built conditionally where the key is dropped.","solutions":["Send a non-empty trimmed roomId in the body: {\"roomId\": \"<room _id>\"}.","Check the key name — this route wants roomId in the body, not rid in the URL.","Assert required fields client-side before posting."],"exampleFix":"// before\nawait api.post('/v1/livechat/room.resumeOnHold', { rid });\n\n// after\nawait api.post('/v1/livechat/room.resumeOnHold', { roomId: rid });","handlingStrategy":"validation","validationCode":"const isNonEmptyRoomId = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;\nif (!isNonEmptyRoomId(roomId)) throw new Error('roomId (body) is required and must be non-blank');\nawait api.post('/v1/livechat/room.resumeOnHold', { roomId });","typeGuard":"const isNonEmptyRoomId = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Note the asymmetry: this route takes roomId in the body, not rid in the URL.","Assert required body fields before posting.","Trim inputs in form layers so whitespace-only values never reach the API."],"tags":["omnichannel","livechat","on-hold","request-validation","rest-api"],"backgroundTag":"missing-required-parameter","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}