RocketChat/Rocket.Chat · error · Error

invalid user

Error message

invalid user

What it means

`CloudAnnouncementsModule` (appId `cloud-announcements-core`) handles Rocket.Chat Cloud announcement banners. Its `viewClosed()` dismisses the announcement for the acting user and notifies Cloud, so it requires `payload.user._id`; when missing it throws a plain `Error('invalid user')` before any dismissal or Cloud call happens.

Source

Thrown at apps/meteor/server/modules/core-apps/cloudAnnouncements.module.ts:59

		return settings.get('Cloud_Url');
	}

	blockAction(payload: UiKitCoreAppBlockActionPayload): Promise<UiKit.ServerInteraction | undefined> {
		return this.handlePayload(payload);
	}

	viewSubmit(payload: UiKitCoreAppViewSubmitPayload): Promise<UiKit.ServerInteraction | undefined> {
		return this.handlePayload(payload);
	}

	async viewClosed(payload: UiKitCoreAppViewClosedPayload): Promise<UiKit.ServerInteraction | undefined> {
		const {
			payload: { view: { viewId, id } = {} },
			user: { _id: userId } = {},
		} = payload;

		if (!userId) {
			throw new Error('invalid user');
		}

		if (!id && !viewId) {
			throw new Error('invalid view');
		}

		if (!payload.triggerId) {
			throw new Error('invalid triggerId');
		}

		// For backwards compatibility: we prefer to use viewId, but some legacy banners
		// may only have id. We fetch all matching banners and prioritize viewId match.
		const bannerIds = [viewId, id].filter((bannerId) => isTruthy(bannerId));
		const banners = await Banners.findByIds(bannerIds).toArray();
		const announcement = banners.find((b) => b._id === viewId) || banners.find((b) => b._id === id);
		if (!announcement) {
			throw new Error('Banner not found');
		}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Include `user: { _id: userId }` in the viewClosed payload.
  2. For programmatic dismissal, call `Banner.dismiss(userId, bannerId)` from `@rocket.chat/core-services` directly instead of going through the module.
  3. Verify the dispatcher (uiKit core app bus) attaches the current user to every interaction it forwards.

Example fix

// before
await announcementsModule.viewClosed({
  appId: 'cloud-announcements-core',
  payload: { view: { viewId: bannerId }, triggerId },
} as any);

// after
await announcementsModule.viewClosed({
  appId: 'cloud-announcements-core',
  user: { _id: userId },
  payload: { view: { viewId: bannerId }, triggerId },
} as any);
Defensive patterns

Strategy: validation

Validate before calling

const canDismiss = (payload: UiKitCoreAppViewClosedPayload): boolean =>
  Boolean(payload.user?._id && (payload.payload?.view?.viewId || payload.payload?.view?.id) && payload.triggerId);

if (canDismiss(payload)) {
  await announcementsModule.viewClosed(payload);
}

Type guard

function isCompleteAnnouncementClosePayload(
  payload: UiKitCoreAppViewClosedPayload,
): payload is UiKitCoreAppViewClosedPayload & { user: { _id: string }; triggerId: string } {
  return Boolean(payload.user?._id && payload.triggerId);
}

Try / catch

try {
  await announcementsModule.viewClosed(payload);
} catch (error) {
  if (error instanceof Error && error.message === 'invalid user') {
    return; // cannot attribute the dismissal to a user
  }
  throw error;
}

Prevention

When it happens

Trigger: A `viewClosed` interaction for a cloud announcement banner where `payload.user` / `payload.user._id` is undefined — e.g. a synthetic payload from custom code, or an interaction dispatched after the user's session context was lost.

Common situations: Custom automation that closes announcement banners by dispatching UiKit events; test payloads built without the user field; stale clients after logout or session expiry.

Related errors


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