{"record":{"id":"8a17a94c73a98777","repo":"RocketChat/Rocket.Chat","slug":"error-action-not-allowed-8a17a9","errorCode":"error-action-not-allowed","errorMessage":"Editing settings is not allowed","messagePattern":"Editing settings is not allowed","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/settings/saveSetting.ts","lineNumber":28,"sourceCode":"import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\nimport { notifyOnSettingChanged } from '../../lib/notifyListener';\nimport { SettingValidationError, validateSettingRules } from '../../lib/settingValidationRules';\nimport { disableCustomScripts } from '../../lib/shared/disableCustomScripts';\nimport { updateAuditedByUser } from '../../settings/lib/auditedSettingUpdates';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tsaveSetting(_id: string, value: SettingValue, editor: SettingEditor): Promise<boolean>;\n\t}\n}\n\nMeteor.methods<ServerMethods>({\n\tsaveSetting: twoFactorRequired(async function (_id: string, value: SettingValue, editor: SettingEditor) {\n\t\tmethodDeprecationLogger.method('saveSetting', '9.0.0', '/v1/settings/:_id');\n\t\tconst uid = Meteor.userId();\n\t\tif (!uid) {\n\t\t\tthrow new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {\n\t\t\t\tmethod: 'saveSetting',\n\t\t\t});\n\t\t}\n\n\t\tif (\n\t\t\t!(await hasPermissionAsync(uid, 'edit-privileged-setting')) &&\n\t\t\t!(await hasAllPermissionAsync(uid, ['manage-selected-settings', getSettingPermissionId(_id)]))\n\t\t) {\n\t\t\t// TODO use the same function\n\t\t\tthrow new Meteor.Error('error-action-not-allowed', 'Editing settings is not allowed', {\n\t\t\t\tmethod: 'saveSetting',\n\t\t\t\tsettingId: _id,\n\t\t\t});\n\t\t}\n\n\t\t// Verify the _id passed in is a string.\n\t\tcheck(_id, String);\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/settings/saveSetting.ts#L10-L46","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard the call: only invoke `saveSetting` after `Meteor.userId()` is non-null (wait for the login sequence).","For programmatic access, use the REST endpoint `PUT /api/v1/settings/:_id` with an auth token instead of the deprecated DDP method.","Re-authenticate (loginWithPassword/loginWithToken) on `connection rejected` / expired-token events before retrying.","Check for a logged-out tab or a second connection with stale credentials when the error appears intermittently."],"exampleFix":"// before\nMeteor.call('saveSetting', _id, value, editor);\n\n// after\nif (!Meteor.userId()) {\n  // wait for login (or redirect to login) before saving settings\n  return;\n}\nMeteor.call('saveSetting', _id, value, editor);","handlingStrategy":"validation","validationCode":"if (!Meteor.userId()) {\n  // wait for the login chain; do not call saveSetting unauthenticated\n  return;\n}\nawait Meteor.callAsync('saveSetting', _id, value, editor);","typeGuard":"const isAuthenticated = (): boolean => typeof Meteor.userId() === 'string';","tryCatchPattern":"try {\n  await Meteor.callAsync('saveSetting', _id, value, editor);\n} catch (e: any) {\n  if (e?.error === 'error-action-not-allowed' && !Meteor.userId()) {\n    // authentication issue, not permissions: re-authenticate and retry once\n  }\n}","preventionTips":["Gate settings saves on a reactive loggedIn state.","Prefer REST /api/v1/settings/:_id with tokens for programmatic access.","Handle token-expiry reconnects by re-authenticating before retrying method calls."],"tags":["meteor","settings","authentication","deprecated"],"backgroundTag":"authentication-required","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}