RocketChat/Rocket.Chat · error · Meteor.Error

error-not-allowed

error-not-allowed

Error message

Threads Disabled

What it means

The readThreads Meteor method (deprecated since 9.0.0 in favor of POST /v1/chat.readThread) throws error-not-allowed with message 'Threads Disabled' when either the connection is not authenticated (Meteor.userId() is null) or the workspace setting Threads_enabled is false. Both conditions share one guard, so an unauthenticated call is also reported as 'Threads Disabled', which can mislead debugging.

Source

Thrown at apps/meteor/server/meteor-methods/messages/readThreads.ts:27

import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { readThread } from '../../lib/messaging/threads/functions';
import { settings } from '../../settings';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		readThreads(tmid: IMessage['_id']): void;
	}
}

Meteor.methods<ServerMethods>({
	async readThreads(tmid) {
		methodDeprecationLogger.method('readThreads', '9.0.0', '/v1/chat.readThread');

		check(tmid, String);

		if (!Meteor.userId() || !settings.get('Threads_enabled')) {
			throw new Meteor.Error('error-not-allowed', 'Threads Disabled', {
				method: 'getThreadMessages',
			});
		}

		const thread = await Messages.findOneById(tmid);
		if (!thread) {
			return;
		}

		const user = (await Meteor.userAsync()) ?? undefined;

		const room = await Rooms.findOneById(thread.rid);
		if (!room) {
			throw new Meteor.Error('error-room-does-not-exist', 'This room does not exist', { method: 'getThreadMessages' });
		}

		if (!(await canAccessRoomAsync(room, user))) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getThreadMessages' });

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Enable threads: Admin -> Workspace -> Threads (the Threads_enabled setting must be true)
  2. Ensure the DDP connection is authenticated before calling (Meteor.userId() must return an id)
  3. Migrate to the supported REST endpoint POST /v1/chat.readThread, the non-deprecated replacement in 9.0.0+
Defensive patterns

Strategy: validation

Validate before calling

// client: gate thread reads on auth + the public Threads_enabled setting
const threadsEnabled = useSetting('Threads_enabled');
if (Meteor.userId() && threadsEnabled) {
	await Meteor.callAsync('readThreads', tmid);
}

Try / catch

try {
	await Meteor.callAsync('readThreads', tmid);
} catch (e: any) {
	if (e?.error === 'error-not-allowed' && e?.reason === 'Threads Disabled') {
		// threads off OR not logged in: hide thread UI, no retry
		return;
	}
	throw e;
}

Prevention

When it happens

Trigger: Meteor.call('readThreads', tmid) while Threads are disabled in Admin -> Workspace -> Threads; the same call from a logged-out DDP connection; test/CI environments running against a fresh database where the Threads_enabled setting was never seeded or explicitly enabled.

Common situations: Workspace admin disabled threads but clients still ship thread UI; upgrade or migration reset the setting to its default; bot or script that never performed a DDP login; automated tests hitting a blank Mongo database.

Related errors


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