RocketChat/Rocket.Chat · error · Error

Invalid app user, cannot transfer

Error message

Invalid app user, cannot transfer

What it means

Thrown by AppLivechatBridge.transferVisitor when Users.findOneByAppId(appId) returns null. Every installed App has a provisioned system user (the app user) used as the transferredBy actor; if it cannot be found the transfer cannot proceed. This points at an App installation/provisioning problem rather than a transfer-input problem.

Source

Thrown at apps/meteor/app/apps/server/bridges/livechat.ts:269

			shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
			shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
		});

		return this.orch.getConverters()?.get('visitors').convertVisitor(livechatVisitor);
	}

	protected async transferVisitor(visitor: IVisitor, transferData: ILivechatTransferData, appId: string): Promise<boolean> {
		this.orch.debugLog(`The App ${appId} is transfering a livechat.`);

		if (!visitor) {
			throw new Error('Invalid visitor, cannot transfer');
		}

		const { targetAgent, targetDepartment: departmentId, currentRoom } = transferData;

		const appUser = await Users.findOneByAppId(appId, {});
		if (!appUser) {
			throw new Error('Invalid app user, cannot transfer');
		}
		const { _id, username, name, type } = appUser;
		const transferredBy = {
			_id,
			username,
			name,
			type,
			userType: 'user',
		} as const;

		let userId;
		let transferredTo;

		if (targetAgent?.id) {
			transferredTo = await Users.findOneAgentById(targetAgent.id, {
				projection: { _id: 1, username: 1, name: 1 },
			});
			if (!transferredTo) {

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Reinstall or re-enable the App so its system user is reprovisioned.
  2. Confirm the App's system user exists in the Users collection (type 'app').
  3. Check the App lifecycle hooks ran on install (enableApp provisioning).
  4. Avoid manually deleting users whose type is 'app'.
Defensive patterns

Strategy: validation

Validate before calling

const appUser = await Users.findOneByAppId(appId, {});
if (!appUser) {
  throw new Error(`App ${appId} is not provisioned (system user missing); reinstall the App`);
}
await app.getLivechatModifier().transferVisitor(visitor, transferData);

Type guard

const isAppProvisioned = async (appId: string): Promise<boolean> =>
  Boolean(await Users.findOneByAppId(appId, { projection: { _id: 1 } }));

Try / catch

try {
  return await app.getLivechatModifier().transferVisitor(visitor, transferData);
} catch (e) {
  if ((e as Error).message === 'Invalid app user, cannot transfer') {
    // prompt reinstall/re-enable of the App, then retry
  }
  throw e;
}

Prevention

When it happens

Trigger: An App invokes the transfer accessor; the bridge looks up its own provisioned user via Users.findOneByAppId(appId) and gets null, e.g. the App's system user was deleted, the appId is stale, or provisioning did not complete.

Common situations: App's system user was removed manually or by a cleanup job; App was partially uninstalled/reinstalled and the user record is missing; appId mismatch after an App re-import; a DB restore that omitted the apps' user documents.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/5d95bb63a2866953. Report an issue: GitHub.