RocketChat/Rocket.Chat · error

The setting " " is not readable.

Error message

The setting "${setting.id}" is not readable.

What it means

updateOne on the settings bridge first asserts isReadableById, which requires the setting to exist and not be flagged secret. Updating an unknown id or a secret setting (passwords, keys) fails with 'The setting ... is not readable.' before any write happens.

Solutions

  1. Confirm the setting id exists on this server version.
  2. Secret settings cannot be updated by apps; have an administrator change them in the UI, or use a non-secret setting.
  3. Re-check ids after server upgrades — settings are renamed or removed across versions.

Example fix

// before
await updateSetting({ id: 'SMTP_Password', value }, appId); // secret -> throws

// after
await updateSetting({ id: 'SMTP_Host', value }, appId); // exists and is not secret
Defensive patterns

Strategy: try-catch

Try / catch

Catch, log the setting id, and skip the update; surface an admin action item when the target is a secret setting.

Prevention

When it happens

Trigger: App updates a server setting whose id does not exist on this server (typo, removed/renamed in another Rocket.Chat version), or updates a setting defined with secret: true.

Common situations: Apps writing configuration such as SMTP/OAuth credentials (secret settings); settings renamed across major server upgrades; case-sensitive id mismatches.

Related errors


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

Appendix: source

Thrown at apps/meteor/app/apps/server/bridges/settings.ts:101

		const readSettings = readSettingsPermission as IReadSettingPermission;
		// If the setting is in the hiddenSettings list (defined within the permission), then it can bypass the hidden flag.
		// If not, then it must be a non-hidden setting. This is to allow apps to read hidden settings if they have the permission to do so.
		const setting = readSettings.hiddenSettings?.includes(id) ? await Settings.findOneById(id) : await Settings.findOneNotHiddenById(id);

		if (!setting) {
			this.orch.debugLog(`The setting ${id} is not found.`);
			return null;
		}

		return this.orch.getConverters()?.get('settings').convertToApp(setting);
	}

	protected async updateOne(setting: ISetting & { id: string }, appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is updating the setting ${setting.id} .`);

		if (!(await this.isReadableById(setting.id, appId))) {
			throw new Error(`The setting "${setting.id}" is not readable.`);
		}

		if (
			(
				await updateAuditedByApp({
					_id: appId,
				})(Settings.updateValueById, setting.id, setting.value)
			).modifiedCount
		) {
			void notifyOnSettingChangedById(setting.id);
		}
	}

	protected async incrementValue(id: string, value: number, appId: string): Promise<void> {
		this.orch.debugLog(`The App ${appId} is incrementing the value of the setting ${id}.`);

		if (!(await this.isReadableById(id, appId))) {
			throw new Error(`The setting "${id}" is not readable.`);

View on GitHub (pinned to b2c16d5842)