RocketChat/Rocket.Chat · error · Meteor.Error

error-action-not-allowed

error-action-not-allowed

Error message

Editing settings is not allowed

What it means

The `saveSetting` Meteor method requires an authenticated DDP session: the very first check after the deprecation log is `Meteor.userId()`, and a null userId throws `error-action-not-allowed` before any permission or value validation. Note the method is wrapped in `twoFactorRequired` and is deprecated since 9.0.0 in favor of the REST endpoint `PUT /v1/settings/:_id`.

Solutions

  1. Guard the call: only invoke `saveSetting` after `Meteor.userId()` is non-null (wait for the login sequence).
  2. For programmatic access, use the REST endpoint `PUT /api/v1/settings/:_id` with an auth token instead of the deprecated DDP method.
  3. Re-authenticate (loginWithPassword/loginWithToken) on `connection rejected` / expired-token events before retrying.
  4. Check for a logged-out tab or a second connection with stale credentials when the error appears intermittently.

Example fix

// before
Meteor.call('saveSetting', _id, value, editor);

// after
if (!Meteor.userId()) {
  // wait for login (or redirect to login) before saving settings
  return;
}
Meteor.call('saveSetting', _id, value, editor);
Defensive patterns

Strategy: validation

Validate before calling

if (!Meteor.userId()) {
  // wait for the login chain; do not call saveSetting unauthenticated
  return;
}
await Meteor.callAsync('saveSetting', _id, value, editor);

Type guard

const isAuthenticated = (): boolean => typeof Meteor.userId() === 'string';

Try / catch

try {
  await Meteor.callAsync('saveSetting', _id, value, editor);
} catch (e: any) {
  if (e?.error === 'error-action-not-allowed' && !Meteor.userId()) {
    // authentication issue, not permissions: re-authenticate and retry once
  }
}

Prevention

When it happens

Trigger: Invoking `Meteor.call('saveSetting', _id, value, editor)` from an unauthenticated connection: before login completes on app boot, after logout, after the DDP resume token expired following a reconnect, or from server-side code that runs without a user context.

Common situations: Client code firing during initial page load before `Meteor.userId()` is set; long-lived sessions whose resume token was invalidated; scripts or integrations calling DDP methods without logging in first; migrations to the REST API missing this endpoint.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/meteor-methods/settings/saveSetting.ts:28

import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { notifyOnSettingChanged } from '../../lib/notifyListener';
import { SettingValidationError, validateSettingRules } from '../../lib/settingValidationRules';
import { disableCustomScripts } from '../../lib/shared/disableCustomScripts';
import { updateAuditedByUser } from '../../settings/lib/auditedSettingUpdates';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		saveSetting(_id: string, value: SettingValue, editor: SettingEditor): Promise<boolean>;
	}
}

Meteor.methods<ServerMethods>({
	saveSetting: twoFactorRequired(async function (_id: string, value: SettingValue, editor: SettingEditor) {
		methodDeprecationLogger.method('saveSetting', '9.0.0', '/v1/settings/:_id');
		const uid = Meteor.userId();
		if (!uid) {
			throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {
				method: 'saveSetting',
			});
		}

		if (
			!(await hasPermissionAsync(uid, 'edit-privileged-setting')) &&
			!(await hasAllPermissionAsync(uid, ['manage-selected-settings', getSettingPermissionId(_id)]))
		) {
			// TODO use the same function
			throw new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {
				method: 'saveSetting',
				settingId: _id,
			});
		}

		// Verify the _id passed in is a string.
		check(_id, String);

View on GitHub (pinned to b2c16d5842)