RocketChat/Rocket.Chat · error · Meteor.Error

error-action-not-allowed

error-action-not-allowed

Error message

Editing user is not allowed

What it means

Thrown by POST users.setPreferences when the caller passes a userId different from their own authenticated id without holding the edit-other-user-info permission. The endpoint edits user preferences; self-service is always allowed, but touching another user requires that admin permission.

Solutions

  1. Omit userId entirely to edit your own preferences
  2. If editing another user is intended, grant the caller edit-other-user-info first (permissions.update)
  3. Check the effective permission via users.info/permissions before showing cross-user edit UI

Example fix

// before
await sdk.post('users.setPreferences', { userId: targetUserId, data }); // fails without permission
// after
if (targetUserId !== myUserId) {
  await requirePermission('edit-other-user-info'); // fails fast client-side
}
await sdk.post('users.setPreferences', { ...(targetUserId !== myUserId && { userId: targetUserId }), data });
Defensive patterns

Strategy: validation

Validate before calling

if (targetUserId && targetUserId !== myUserId) {
  const allowed = await hasPermission('edit-other-user-info');
  if (!allowed) throw new Error('cross-user preferences edit requires edit-other-user-info');
}
await sdk.post('users.setPreferences', { ...(targetUserId && targetUserId !== myUserId ? { userId: targetUserId } : {}), data });

Try / catch

catch (e) { if (e?.error === 'error-action-not-allowed') retryAsSelfOrRequestPermission(); else throw e; }

Prevention

When it happens

Trigger: POST users.setPreferences with {userId: 'otherUserId', data: {...}} from an account lacking edit-other-user-info; passing a userId that equals your own is fine, so this fires only on cross-user edits; permission revoked between UI load and save.

Common situations: Admin-panel-like tools assuming all logged-in users may edit anyone; cached auth tokens from a demoted admin; passing the target user's id in a field the schema still accepts even when not intended.

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


AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18). Data as JSON: /api/errors/3162458f29d65b14. Report an issue: GitHub.

Appendix: source

Thrown at apps/meteor/server/api/v1/users.ts:246

	)
	.post(
		'users.setPreferences',
		{
			authRequired: true,
			body: isUsersSetPreferencesParamsPOST,
			response: {
				200: userObjectResponse,
				400: validateBadRequestErrorResponse,
				401: validateUnauthorizedErrorResponse,
			},
		},
		async function action() {
			if (
				this.bodyParams.userId &&
				this.bodyParams.userId !== this.userId &&
				!(await hasPermissionAsync(this.user, 'edit-other-user-info'))
			) {
				throw new Meteor.Error('error-action-not-allowed', 'Editing user is not allowed');
			}
			const userId = this.bodyParams.userId ? this.bodyParams.userId : this.userId;
			if (!(await Users.findOneById(userId))) {
				throw new Meteor.Error('error-invalid-user', 'The optional "userId" param provided does not match any users');
			}

			const { statusVisibilityDenied: _ownBlockList, ...preferences } = this.bodyParams.data;

			await saveUserPreferences(userId === this.userId ? this.bodyParams.data : preferences, userId);
			const user = await Users.findOneById(userId, {
				projection: {
					'settings.preferences': 1,
					'language': 1,
				},
			});

			if (!user) {
				return API.v1.failure('User not found');

View on GitHub (pinned to e4b8178b20)