RocketChat/Rocket.Chat · error · MeteorError

error-invalid-user

error-invalid-user

Error message

Invalid user provided for erasing team

What it means

eraseTeamShared (backing team deletion flows) requires the acting user object; a falsy user argument triggers MeteorError('Invalid user provided for erasing team', 'error-invalid-user') from @rocket.chat/core-services, where the first argument is the message and the second the machine-readable code. The guard runs before any destructive operation (rooms are only erased after it).

Source

Thrown at apps/meteor/server/api/lib/eraseTeam.ts:23

import { eraseRoom } from '../../lib/eraseRoom';
import { SystemLogger } from '../../lib/logger/system';
import { deleteRoom } from '../../lib/rooms/deleteRoom';

type EraseRoomFnType = <T extends AtLeast<IUser, '_id' | 'name' | 'username' | 'roles'>>(rid: string, user: T) => Promise<boolean | void>;

export const eraseTeamShared = async <T extends AtLeast<IUser, '_id' | 'name' | 'username' | 'roles'>>(
	user: T,
	team: ITeam,
	roomsToRemove: IRoom['_id'][] = [],
	eraseRoomFn: EraseRoomFnType,
) => {
	const rooms: string[] = roomsToRemove.length
		? (await Team.getMatchingTeamRooms(team._id, roomsToRemove)).filter((roomId) => roomId !== team.roomId)
		: [];

	if (!user) {
		throw new MeteorError('Invalid user provided for erasing team', 'error-invalid-user', {
			method: 'eraseTeamShared',
		});
	}

	// If we got a list of rooms to delete along with the team, remove them first
	await Promise.all(rooms.map((room) => eraseRoomFn(room, user)));

	// Move every other room back to the workspace
	await Team.unsetTeamIdOfRooms(user, team);

	// Remove the team's main room
	await eraseRoomFn(team.roomId, user);

	// Delete all team memberships
	await Team.removeAllMembersFromTeam(team._id);

	// And finally delete the team itself
	await Team.deleteById(team._id);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Load and null-check the acting user before calling eraseTeamShared.
  2. If the original actor may be gone, pass a valid admin/bot user as the actor for the audit trail.
  3. Return early with a clear 'user not found' error instead of letting the shared helper throw.

Example fix

// before
const user = await Users.findOneById(uid);
await eraseTeamShared(user, team, roomsToRemove, eraseRoom);

// after
const user = await Users.findOneById(uid);
if (!user) throw new Meteor.Error('error-invalid-user', 'Requesting user not found');
await eraseTeamShared(user, team, roomsToRemove, eraseRoom);
Defensive patterns

Strategy: type-guard

Validate before calling

const user = await Users.findOneById(uid);
if (!user) {
	// do not call eraseTeamShared; surface 'acting user not found'
}

Type guard

function isFullUser<T extends Partial<IUser>>(u: T | null | undefined): u is T & { _id: string; username: string; roles: string[] } {
	return !!u && typeof u._id === 'string' && typeof u.username === 'string' && Array.isArray(u.roles);
}

Try / catch

try {
	await eraseTeamShared(user, team, roomsToRemove, eraseRoom);
} catch (e: any) {
	if (e?.error === 'error-invalid-user') {
		// acting user missing: reload/fall back to an admin actor and retry
	}
	throw e;
}

Prevention

When it happens

Trigger: Server code calling eraseTeamShared with null/undefined as the user — e.g. a Users.findOneById result used without a null check, or the requesting user being deleted between request validation and execution.

Common situations: Races where the acting user is deleted mid-operation, test/migration code passing partial objects, refactors that made the user parameter optional at the call site.

Related errors


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