RocketChat/Rocket.Chat · error · Meteor.Error

error-direct-message-room

error-direct-message-room

Error message

rooms type: ${room.t} can not be archived

What it means

Thrown when the room exists but its type coordinator refuses the ARCHIVE member action: roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.ARCHIVE, userId) returned false. Despite the legacy code error-direct-message-room, this guards every room type, not only direct messages - any type whose directives do not allow archiving for this user lands here.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/archiveRoom.ts:34

		archiveRoom(rid: string): Promise<void>;
	}
}

export const executeArchiveRoom = async (userId: string, rid: string) => {
	check(rid, String);

	const user = await Users.findOneById(userId, { projection: { username: 1, name: 1 } });
	if (!user || !isRegisterUser(user)) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
	}

	const room = await Rooms.findOneById(rid);
	if (!room) {
		throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'archiveRoom' });
	}

	if (!(await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.ARCHIVE, userId))) {
		throw new Meteor.Error('error-direct-message-room', `rooms type: ${room.t} can not be archived`, { method: 'archiveRoom' });
	}

	if (!(await hasPermissionAsync(userId, 'archive-room', room._id))) {
		throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'archiveRoom' });
	}

	return archiveRoom(rid, user);
};

Meteor.methods<ServerMethods>({
	async archiveRoom(rid) {
		methodDeprecationLogger.method('archiveRoom', '9.0.0', '/v1/channels.archive');
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
		}

		return executeArchiveRoom(userId, rid);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Filter the archive action by room type before invoking: stock archivable types are 'c' (channel) and 'p' (private group).
  2. For direct messages, use hide/close conversation instead of archive.
  3. If you maintain a custom room type, implement allowMemberAction for RoomMemberActions.ARCHIVE in its coordinator.

Example fix

// before
Meteor.call('archiveRoom', room._id); // fires for DMs too

// after
const isArchivable = (t: string) => t === 'c' || t === 'p';
if (isArchivable(room.t)) {
  Meteor.call('archiveRoom', room._id);
} else {
  Meteor.call('hideRoom', room._id);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const room = roomsCollection.findOne({ _id: rid }, { fields: { t: 1 } });
if (room && !isArchivableRoom(room)) {
  // this room type cannot be archived - offer hide/close instead
}

Type guard

const isArchivableRoom = (room: { t: string }): room is { t: 'c' | 'p' } =>
  room.t === 'c' || room.t === 'p';

Try / catch

try {
  await Meteor.callAsync('archiveRoom', rid);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-direct-message-room') {
    // room type does not support archive - hide the conversation instead
    await Meteor.callAsync('hideRoom', rid);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Archiving a direct-message room (t='d'); archiving room types whose directives do not register the ARCHIVE member action for the caller; custom room types (apps/forks) whose coordinator omits allowMemberAction for ARCHIVE.

Common situations: UIs that enable archive for every listed room including DMs; scripts walking all subscriptions and archiving every rid; custom room types added without archive support.

Related errors


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