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
- Load and null-check the acting user before calling eraseTeamShared.
- If the original actor may be gone, pass a valid admin/bot user as the actor for the audit trail.
- 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
- Never pass an unchecked Users.findOneById result into destructive team flows
- Fall back to a designated admin/bot actor when the original requester may have been deleted
- In tests/migrations, construct the full AtLeast<IUser, '_id'|'name'|'username'|'roles'> shape explicitly
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.