RocketChat/Rocket.Chat · error
Invalid user
Error message
Invalid user
What it means
When creating a private group, AppRoomBridge.createPrivateGroup resolves the creator with Users.findOneById(userId) and throws 'Invalid user' when no user document matches room.creator.id — unlike the channel/DM paths, the private-group creation method needs the full user object, so the creator must exist.
Solutions
- Verify the creator exists first (e.g. read.getUserById) before attempting to create the private group
- Prefer the current user from the execution context over persisted ids
- Re-fetch a fresh room/user reference instead of reusing long-lived cached objects
- Surface a clear app-level error instead of letting the bridge throw
Example fix
// before
const room = { type: RoomType.PRIVATE_GROUP, creator: { id: staleUserId }, name: 'ops' };
await app.getRocketChat().getChat().createRoom(room, members); // staleUserId deleted -> throws
// after
const creator = await this.read.getUserById(staleUserId);
if (!creator) throw new Error('creator no longer exists');
const room = { type: RoomType.PRIVATE_GROUP, creator, name: 'ops' };
await app.getRocketChat().getChat().createRoom(room, members); Defensive patterns
Strategy: validation
Validate before calling
const creator = await this.read.getUserById(room.creator.id);
if (!creator) {
throw new Error(`cannot create private group: creator ${room.creator.id} not found`);
}
await app.getRocketChat().getChat().createRoom({ ...room, creator }, members); Try / catch
try {
await app.getRocketChat().getChat().createRoom(room, members);
} catch (err) {
if (err instanceof Error && err.message === 'Invalid user') {
app.getLogger().warn('private group creator no longer exists');
return;
}
throw err;
} Prevention
- Resolve creators through the user reader right before creating private groups
- Prefer the contextual user (e.g. from the action/event context) over persisted ids
- Re-validate persisted user ids on app upgrade or workspace migration
- Remember only the private-group path checks the creator — channels/DMs fail later with different errors
When it happens
Trigger: room.creator.id references a deleted user; an app passes its own bot user id while the bot user is missing; creator id copied from stale cached data, a different workspace, or a federation/import mismatch.
Common situations: Users deleted between when the app captured the id and when it creates the group; dev databases reset while the app kept persisted creator ids; typos in hand-built creator objects.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- Only channels, private groups and direct messages can be…
- error-app-prevented
- error-app-prevented
- error-app-prevented-deleting
- error-invalid-custom-field-value
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/6bc672c6795671f4.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/app/apps/server/bridges/rooms.ts:100
delete extraData.t;
delete extraData.ro;
delete extraData.customFields;
return extraData;
}
private async createChannel(userId: string, room: ICoreRoom, members: string[]): Promise<string> {
return (await createChannelMethod(userId, room.name || '', members, room.ro, room.customFields, this.prepareExtraData(room))).rid;
}
private async createDirectMessage(userId: string, members: string[]): Promise<string> {
return (await createDirectMessage(members, userId)).rid;
}
private async createPrivateGroup(userId: string, room: ICoreRoom, members: string[]): Promise<string> {
const user = await Users.findOneById(userId);
if (!user) {
throw new Error('Invalid user');
}
return (await createPrivateGroupMethod(user, room.name || '', members, room.ro, room.customFields, this.prepareExtraData(room))).rid;
}
protected async getById(roomId: string, appId: string): Promise<IRoom> {
this.orch.debugLog(`The App ${appId} is getting the roomById: "${roomId}"`);
// #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed.
const promise: Promise<IRoom | undefined> = this.orch.getConverters()?.get('rooms').convertById(roomId);
return promise as Promise<IRoom>;
}
protected async getByName(roomName: string, appId: string): Promise<IRoom> {
this.orch.debugLog(`The App ${appId} is getting the roomByName: "${roomName}"`);
// #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed.
const promise: Promise<IRoom | undefined> = this.orch.getConverters()?.get('rooms').convertByName(roomName);
return promise as Promise<IRoom>;View on GitHub (pinned to b2c16d5842)