RocketChat/Rocket.Chat · error · Error

invalid banner

Error message

invalid banner

What it means

`BannerModule.viewClosed` (`banner-core` core app) throws `Error('invalid banner')` when the nested `payload.payload.view.viewId` is missing. The viewId identifies which banner record to dismiss via `Banner.dismiss(userId, bannerId)`, so a close event without it cannot be routed to a banner.

Source

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

import type { IUiKitCoreApp, UiKitCoreAppViewClosedPayload } from '@rocket.chat/core-services';
import type * as UiKit from '@rocket.chat/ui-kit';

export class BannerModule implements IUiKitCoreApp {
	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. Send the banner's identifier as `payload: { view: { viewId } }` in the viewClosed payload.
  2. Fetch the active banner (e.g. via the banners API) and use its `viewId` when building the close interaction.
  3. For server-side dismissal without a UI surface, call `Banner.dismiss(userId, bannerId)` directly.

Example fix

// before
payload: { view: { id: bannerId }, triggerId }

// after
payload: { view: { viewId: bannerId }, triggerId }
Defensive patterns

Strategy: validation

Validate before calling

if (!payload.payload?.view?.viewId) {
  throw new Error('viewId is required to close a banner-core banner');
}
await bannerModule.viewClosed(payload);

Type guard

const hasBannerViewId = (
  payload: UiKitCoreAppViewClosedPayload,
): payload is UiKitCoreAppViewClosedPayload & { payload: { view: { viewId: string } } } =>
  Boolean(payload.payload?.view?.viewId);

Try / catch

try {
  await bannerModule.viewClosed(payload);
} catch (error) {
  if (error instanceof Error && error.message === 'invalid banner') {
    // rebuild the payload with view.viewId and retry
  }
}

Prevention

When it happens

Trigger: A `viewClosed` interaction for `banner-core` where `view` or `view.viewId` is undefined — e.g. a modal-close payload shape (which carries `view.id`) reused for a banner, or a manually constructed payload that omits the view.

Common situations: Frontend code or apps sending `view: { id }` instead of `view: { viewId }`; payloads stripped by a JSON round-trip or logger; test fixtures that only set triggerId.

Related errors


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