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
- Omit userId entirely to edit your own preferences
- If editing another user is intended, grant the caller edit-other-user-info first (permissions.update)
- 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
- Default to omitting userId for self-edits
- Gate cross-user edit UI on a live permission check, not a cached role
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)