{"record":{"id":"6a1754d3e2966a4f","repo":"RocketChat/Rocket.Chat","slug":"error-action-not-allowed-6a1754","errorCode":"error-action-not-allowed","errorMessage":"Editing email verification is not allowed","messagePattern":"Editing email verification is not allowed","errorType":"exception","errorClass":"MeteorError","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/users/saveUser/validateUserData.ts","lineNumber":19,"sourceCode":"import { MeteorError } from '@rocket.chat/core-services';\nimport type { IUser } from '@rocket.chat/core-typings';\nimport { makeFunction } from '@rocket.chat/patch-injection';\nimport escape from 'lodash.escape';\n\nimport type { SaveUserData } from './saveUser';\nimport { isUpdateUserData } from './saveUser';\nimport { trim } from '../../../../lib/utils/stringUtils';\nimport { settings } from '../../../settings';\nimport { getRoleIds } from '../../authorization/getRoles';\nimport { hasPermissionAsync } from '../../authorization/hasPermission';\nimport { checkEmailAvailability } from '../checkEmailAvailability';\nimport { checkUsernameAvailability } from '../checkUsernameAvailability';\n\nexport const validateUserData = makeFunction(async (userId: IUser['_id'], userData: SaveUserData): Promise<void> => {\n\tconst existingRoles = await getRoleIds();\n\n\tif (userData.verified && userData._id && userId === userData._id) {\n\t\tthrow new MeteorError('error-action-not-allowed', 'Editing email verification is not allowed', {\n\t\t\tmethod: 'insertOrUpdateUser',\n\t\t\taction: 'Editing_user',\n\t\t});\n\t}\n\n\tif (isUpdateUserData(userData) && userId !== userData._id && !(await hasPermissionAsync(userId, 'edit-other-user-info'))) {\n\t\tthrow new MeteorError('error-action-not-allowed', 'Editing user is not allowed', {\n\t\t\tmethod: 'insertOrUpdateUser',\n\t\t\taction: 'Editing_user',\n\t\t});\n\t}\n\n\tif (!isUpdateUserData(userData) && !(await hasPermissionAsync(userId, 'create-user'))) {\n\t\tthrow new MeteorError('error-action-not-allowed', 'Adding user is not allowed', {\n\t\t\tmethod: 'insertOrUpdateUser',\n\t\t\taction: 'Adding_user',\n\t\t});\n\t}","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/users/saveUser/validateUserData.ts#L1-L37","documentation":"Thrown by validateUserData() during saveUser (reached via POST /api/v1/users.update or the admin Users screen) when a caller submits the 'verified' flag for their OWN account. The server treats self-service email verification as a security bypass: letting a user mark their own email verified defeats verification, so the payload is rejected outright with code error-action-not-allowed. Only users whose _id differs from the edited user's _id (i.e. admins acting on others) may set this flag.","triggerScenarios":"Calling users.update with bodyParams containing both a truthy 'verified' field and an '_id' equal to the authenticated caller's userId. Example: a logged-in user editing their own profile through a custom client that mirrors the full admin form payload, including the verified checkbox.","commonSituations":"Custom profile-edit forms that reuse the admin user-edit payload verbatim; federation/migration scripts that copy the whole user document; a UI bug that serializes a false-but-present 'verified' field (truthiness check means 'verified: true' is the trigger; the field must simply not be sent).","solutions":["Remove 'verified' from the payload when the caller edits their own account (the check is userId === userData._id, so self-edits must never carry it).","If self-service verification is the goal, use the proper verification flow (send a confirmation email via the sendVerificationEmail API) instead of setting the flag.","If an admin legitimately needs to set another user's verified flag, ensure the payload targets a different _id and the caller holds edit-other-user-info (enforced by the next check in the same file)."],"exampleFix":"// before (self edit -> throws)\nawait POST '/api/v1/users.update', { userId: myId, data: { verified: true, name: 'New Name' } };\n\n// after (self edit: omit verified; verify via email flow instead)\nawait POST '/api/v1/users.update', { userId: myId, data: { name: 'New Name' } };\nawait POST '/api/v1/users.sendVerificationEmail';","handlingStrategy":"validation","validationCode":"const editingSelf = payload._id === currentUserId;\nif (editingSelf) {\n  delete payload.verified; // never self-send the verified flag\n}","typeGuard":"const isSafeSelfEdit = (p: { _id?: string; verified?: boolean }, uid: string) =>\n  !(p.verified && p._id && p._id === uid);","tryCatchPattern":"catch (e) {\n  if (e.error === 'error-action-not-allowed' && /email verification/i.test(e.reason)) {\n    // strip 'verified' and resubmit without it\n  }\n}","preventionTips":["Never mirror the admin form payload for self-edits; whitelist editable fields (name, statusText, bio, nickname).","Treat 'verified' as an admin-only field at the client boundary.","Log userId vs userData._id on 403s to catch wrong-token bugs early."],"tags":["users","permissions","email-verification","save-user"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}