RocketChat/Rocket.Chat · error · Error

invalid triggerId

Error message

invalid triggerId

What it means

`BannerModule.viewClosed` (`banner-core`) throws `Error('invalid triggerId')` when `payload.triggerId` is falsy. The triggerId routes the server interaction response (`banner.close`) back to the client surface that opened the banner, so without it the response has nowhere to go and the guard fires before `Banner.dismiss` runs.

Source

Thrown at apps/meteor/server/modules/core-apps/banner.module.ts:24

	appId = 'banner-core';

	// when banner view is closed we need to dismiss that banner for that user
	async viewClosed(payload: UiKitCoreAppViewClosedPayload): Promise<UiKit.ServerInteraction> {
		const {
			payload: { view: { viewId: bannerId } = {} },
			user: { _id: userId } = {},
		} = payload;

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

		if (!bannerId) {
			throw new Error('invalid banner');
		}

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

		await Banner.dismiss(userId, bannerId);

		return {
			type: 'banner.close',
			triggerId: payload.triggerId,
			appId: payload.appId,
			viewId: bannerId,
		};
	}
}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Forward the `triggerId` generated by the originating banner surface in the viewClosed payload.
  2. If you only need server-side dismissal (no response surface), call `Banner.dismiss(userId, bannerId)` directly — no triggerId is required.

Example fix

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

// after
await bannerModule.viewClosed({ appId: 'banner-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 for banner close interactions');
}
await bannerModule.viewClosed(payload);

Type guard

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

Try / catch

try {
  await bannerModule.viewClosed(payload);
} catch (error) {
  if (error instanceof Error && error.message === 'invalid triggerId') {
    // dismiss directly instead: await Banner.dismiss(userId, bannerId)
  }
}

Prevention

When it happens

Trigger: Dispatching a banner close interaction without a `triggerId` — e.g. synthetic interactions built by hand, payloads replayed from logs with the field stripped, or an old client version that does not forward the trigger.

Common situations: Programmatic dismissal attempts that mimic the UI payload incompletely; test harnesses that omit trigger metadata; middleware or serializers that drop falsy-looking fields.

Related errors


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