{"record":{"id":"a8edf78e53493784","repo":"RocketChat/Rocket.Chat","slug":"error-users-params-not-provided","errorCode":"error-users-params-not-provided","errorMessage":"Please provide \"userId\" or \"username\" or \"userIds\" or \"usernames\" as param","messagePattern":"Please provide \"userId\" or \"username\" or \"userIds\" or \"usernames\" as param","errorType":"validation","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/lib/getUserFromParams.ts","lineNumber":58,"sourceCode":"\nexport async function getUserListFromParams(params: {\n\tuserId?: string;\n\tusername?: string;\n\tuser?: string;\n\tuserIds?: string[];\n\tusernames?: string[];\n}): Promise<Pick<IUser, '_id' | 'username'>[]> {\n\t// if params.userId is provided, include it as well\n\tconst soleUser = params.userId || params.username || params.user;\n\tlet userListParam = params.userIds || params.usernames || [];\n\tuserListParam.push(soleUser || '');\n\tuserListParam = userListParam.filter(Boolean);\n\n\t// deduplicate to avoid errors\n\tuserListParam = [...new Set(userListParam)];\n\n\tif (!userListParam.length) {\n\t\tthrow new Meteor.Error('error-users-params-not-provided', 'Please provide \"userId\" or \"username\" or \"userIds\" or \"usernames\" as param');\n\t}\n\n\tif (params.userIds || params.userId) {\n\t\treturn Users.findByIds(userListParam, { projection: { username: 1 } }).toArray();\n\t}\n\n\treturn Users.findByUsernamesIgnoringCase(userListParam, { projection: { username: 1 } }).toArray();\n}\n\n/**\n * Resolves a list of usernames from the request params without requiring the users to\n * already exist locally. `username`/`usernames`/`user` are passed through verbatim, while\n * `userId`/`userIds` are resolved to their usernames via the database.\n *\n * Unlike `getUserListFromParams`, this does not drop usernames that have no local record yet\n * — which is what federation invites rely on: the federated user record is created lazily\n * inside `addUsersToRoomMethod`.\n */","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/lib/getUserFromParams.ts#L40-L76","documentation":"Thrown by getUserListFromParams when, after merging the single-user params (userId/username/user) with the array params (userIds/usernames), filtering out falsy entries and deduplicating, the resulting list is empty. In other words the request carried none of the accepted user identifier params, or only empty/whitespace values that filter(Boolean) removed (note the helper pushes soleUser || '' and then strips empty strings).","triggerScenarios":"POST/GET to an endpoint using this helper (e.g. bulk member operations routed through apps/meteor/server/api/v1/{channels,groups,im,users}.ts) with an empty body, with userIds=[] / usernames=[] empty arrays, or with whitespace-only strings. A typo'd param name (user_id, userName) is silently ignored and produces the same throw.","commonSituations":"Frontend multi-select submitted with nothing chosen so the client posts {userIds: []}; param name mismatch after an API client refactor; query string not URL-encoded so the value arrives empty; integration code that conditionally builds params and skips all branches.","solutions":["Include at least one non-empty value among userId, username, user, userIds, usernames","Guard on the client: skip the API call (or disable the submit button) when the combined identifier list is empty","Check exact param spelling and casing against the endpoint docs — unknown params are ignored, not rejected","URL-encode array params properly (userIds[]=a&userIds[]=b) so they survive the query string"],"exampleFix":"// before\nPOST /api/v1/channels.addAllRoles { \"roomId\": \"abc\", \"userIds\": [] }\n\n// after\nPOST /api/v1/channels.addAllRoles { \"roomId\": \"abc\", \"userIds\": [\"aobEdbYhXfu5hkeqG\"] }","handlingStrategy":"validation","validationCode":"function hasUserListParam(p: { userId?: string; username?: string; user?: string; userIds?: string[]; usernames?: string[] }): boolean {\n  const sole = [p.userId, p.username, p.user].some((v) => typeof v === 'string' && v.trim() !== '');\n  const list = [...(p.userIds ?? []), ...(p.usernames ?? [])].some((v) => typeof v === 'string' && v.trim() !== '');\n  return sole || list;\n}\n// if (!hasUserListParam(params)) skip the request with a client-side error","typeGuard":null,"tryCatchPattern":"try {\n  await client.post('/api/v1/channels.addAllRoles', body);\n} catch (e: any) {\n  if (e?.response?.data?.errorType === 'error-users-params-not-provided') {\n    throw new ValidationError('at least one of userId/username/user/userIds/usernames is required');\n  }\n  throw e;\n}","preventionTips":["Disable submit actions when the selected-users list is empty","Mirror the server's precedence (userId > username > user; arrays merged) when building params","Spell params exactly as documented; unknown keys are silently ignored server-side"],"tags":["rest-api","missing-param","validation","meteor-error"],"backgroundTag":"missing-required-parameter","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T08:17:14.275Z"}