RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-user
error-invalid-user
Error message
Invalid user
What it means
Thrown by executeArchiveRoom when the acting user cannot be used for archiving: Users.findOneById(userId) found no document, or the document fails isRegisterUser (not a plain active registered user, e.g. app/bot or deactivated accounts). Rocket.Chat requires a registered user because that user is recorded as archiving the room. The DDP wrapper has already confirmed a logged-in connection before calling this helper, so hitting this line means the account behind the session is missing or not a regular registered user.
Source
Thrown at apps/meteor/server/meteor-methods/rooms/archiveRoom.ts:25
import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';
import { hasPermissionAsync } from '../../lib/authorization/hasPermission';
import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { archiveRoom } from '../../lib/rooms/archiveRoom';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
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);
};
View on GitHub (pinned to b2c16d5842)
Solutions
- Verify the acting account first: it must exist and satisfy isRegisterUser (plain user type, active).
- If the session went stale, log out and back in so the connection carries a fresh, valid userId.
- Replace the deprecated DDP method with POST /api/v1/channels.archive using valid X-Auth-Token/X-User-Id headers.
Example fix
// before
await executeArchiveRoom(appUserId, rid); // appUserId belongs to an app/bot account
// after
const user = await Users.findOneById(userId, { projections: { type: 1, active: 1 } });
if (!user || !isRegisterUser(user)) {
throw new Error('Acting user is not a registered user');
}
await executeArchiveRoom(userId, rid); Defensive patterns
Strategy: try-catch
Validate before calling
const me = Meteor.user();
if (!me) {
// no account on this connection - re-login before calling archiveRoom
} Type guard
const isRegisteredAccount = (u: { type?: string; active?: boolean } | null | undefined): u is { type: string; active: boolean } =>
!!u && u.type === 'user' && u.active === true; Try / catch
try {
await Meteor.callAsync('archiveRoom', rid);
} catch (e) {
if (e instanceof Meteor.Error && e.error === 'error-invalid-user') {
// account missing or not registered: end the session and re-authenticate
await Meteor.logout();
goToLogin();
return;
}
throw e;
} Prevention
- Always derive the acting user from Meteor.userId() on a live connection instead of passing stored ids.
- Never use app/bot or system accounts for member actions like archiveRoom; use a registered human account or the REST API.
- Treat error-invalid-user arriving after a previously valid session as an account problem, not a transient glitch - verify with users.info.
When it happens
Trigger: Calling Meteor.call('archiveRoom', rid) while the acting account was deleted or deactivated after login; server-side reuse of executeArchiveRoom(userId, rid) with a nonexistent or app/bot user _id; test fixtures that insert users without the active/type fields isRegisterUser requires.
Common situations: Custom integrations passing an app or system user id; admin deactivating a user while their session is still alive; LDAP/OAuth accounts disabled upstream but sessions not purged; unit tests seeding bare user records.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/d59a0005136e3ccb.
Report an issue: GitHub.