RocketChat/Rocket.Chat · error · MeteorError
error-action-not-allowed
error-action-not-allowed
Error message
Editing email verification is not allowed
What it means
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.
Source
Thrown at apps/meteor/server/lib/users/saveUser/validateUserData.ts:19
import { MeteorError } from '@rocket.chat/core-services';
import type { IUser } from '@rocket.chat/core-typings';
import { makeFunction } from '@rocket.chat/patch-injection';
import escape from 'lodash.escape';
import type { SaveUserData } from './saveUser';
import { isUpdateUserData } from './saveUser';
import { trim } from '../../../../lib/utils/stringUtils';
import { settings } from '../../../settings';
import { getRoleIds } from '../../authorization/getRoles';
import { hasPermissionAsync } from '../../authorization/hasPermission';
import { checkEmailAvailability } from '../checkEmailAvailability';
import { checkUsernameAvailability } from '../checkUsernameAvailability';
export const validateUserData = makeFunction(async (userId: IUser['_id'], userData: SaveUserData): Promise<void> => {
const existingRoles = await getRoleIds();
if (userData.verified && userData._id && userId === userData._id) {
throw new MeteorError('error-action-not-allowed', 'Editing email verification is not allowed', {
method: 'insertOrUpdateUser',
action: 'Editing_user',
});
}
if (isUpdateUserData(userData) && userId !== userData._id && !(await hasPermissionAsync(userId, 'edit-other-user-info'))) {
throw new MeteorError('error-action-not-allowed', 'Editing user is not allowed', {
method: 'insertOrUpdateUser',
action: 'Editing_user',
});
}
if (!isUpdateUserData(userData) && !(await hasPermissionAsync(userId, 'create-user'))) {
throw new MeteorError('error-action-not-allowed', 'Adding user is not allowed', {
method: 'insertOrUpdateUser',
action: 'Adding_user',
});
}View on GitHub (pinned to b2c16d5842)
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).
Example fix
// before (self edit -> throws)
await POST '/api/v1/users.update', { userId: myId, data: { verified: true, name: 'New Name' } };
// after (self edit: omit verified; verify via email flow instead)
await POST '/api/v1/users.update', { userId: myId, data: { name: 'New Name' } };
await POST '/api/v1/users.sendVerificationEmail'; Defensive patterns
Strategy: validation
Validate before calling
const editingSelf = payload._id === currentUserId;
if (editingSelf) {
delete payload.verified; // never self-send the verified flag
} Type guard
const isSafeSelfEdit = (p: { _id?: string; verified?: boolean }, uid: string) =>
!(p.verified && p._id && p._id === uid); Try / catch
catch (e) {
if (e.error === 'error-action-not-allowed' && /email verification/i.test(e.reason)) {
// strip 'verified' and resubmit without it
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- error-action-not-allowed
- error-not-authorized-federation
- error-action-not-allowed
- error-not-allowed
- error-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/6a1754d3e2966a4f.
Report an issue: GitHub.