RocketChat/Rocket.Chat · error · Error

invalid triggerId

Error message

invalid triggerId

What it means

`CloudAnnouncementsModule.viewClosed` throws `Error('invalid triggerId')` when `payload.triggerId` is falsy. The triggerId is needed both to acknowledge the close to Cloud (the interaction is forwarded via `handlePayload`) and to address the `banner.close`/`modal.close` response back to the client surface, so the guard fires before dismissal.

Source

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

		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');
		}

		await Banner.dismiss(userId, announcement._id);

		const type = announcement.surface === 'banner' ? 'banner.close' : 'modal.close';

		// for viewClosed we just need to let Cloud know that the banner was closed, no need to wait for the response

		void this.handlePayload(payload);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Forward the `triggerId` from the surface that displayed the announcement in the viewClosed payload.
  2. For dismissal without a UI surface, call `Banner.dismiss(userId, bannerId)` directly — no triggerId needed.

Example fix

// before
await module.viewClosed({ appId: 'cloud-announcements-core', user: { _id: userId }, payload: { view: { viewId } } } as any);

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

Strategy: validation

Validate before calling

if (!payload.triggerId) {
  throw new Error('triggerId is required to close a cloud announcement');
}
await announcementsModule.viewClosed(payload);

Type guard

const hasTrigger = (
  payload: UiKitCoreAppViewClosedPayload,
): payload is UiKitCoreAppViewClosedPayload & { triggerId: string } =>
  typeof payload.triggerId === 'string' && payload.triggerId.length > 0;

Try / catch

try {
  await announcementsModule.viewClosed(payload);
} catch (error) {
  if (error instanceof Error && error.message === 'invalid triggerId') {
    // fall back to Banner.dismiss(userId, bannerId) without notifying the surface
  }
}

Prevention

When it happens

Trigger: Closing a cloud announcement banner via a viewClosed interaction that carries no `triggerId` — e.g. manually constructed payloads, replayed log entries, or an outdated client that does not propagate the trigger.

Common situations: Custom scripts mimicking the banner close flow; test doubles omitting trigger metadata; intermediate layers dropping fields.

Related errors


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