RocketChat/Rocket.Chat · warning · Error

Banner not found

Error message

Banner not found

What it means

`CloudAnnouncementsModule.viewClosed` resolves candidate ids `[viewId, id]`, queries `Banners.findByIds(bannerIds)`, and picks the first record matching `viewId` (or `id` for legacy banners). If none of the candidates exist in the Banners collection it throws `Error('Banner not found')`, meaning the referenced announcement banner no longer exists locally — it expired, was removed by a cloud sync, or the client sent a stale/wrong id.

Source

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

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

		return {
			type,
			triggerId: payload.triggerId,
			appId: payload.appId,
			viewId: announcement._id,
		};
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-fetch the current banners for the user and dismiss using the live banner `_id`.
  2. Verify existence before dismissing: `await Banners.findByIds([viewId, id].filter(Boolean)).toArray()`.
  3. Treat this error as benign when the banner was legitimately removed (expired/already dismissed) and drop the interaction.
Defensive patterns

Strategy: validation

Validate before calling

import { Banners } from '@rocket.chat/models';
import { isTruthy } from '@rocket.chat/tools';

const bannerIds = [viewId, id].filter(isTruthy);
const banners = await Banners.findByIds(bannerIds).toArray();
if (!banners.length) {
  // banner already removed/expired: skip dismissal
} else {
  await announcementsModule.viewClosed(payload);
}

Try / catch

try {
  await announcementsModule.viewClosed(payload);
} catch (error) {
  if (error instanceof Error && error.message === 'Banner not found') {
    return; // benign: banner expired or was already dismissed
  }
  throw error;
}

Prevention

When it happens

Trigger: Dismissing a cloud announcement whose `_id` is not in the local Banners collection — e.g. the announcement expired or was replaced between render and close; a duplicate dismiss after the banner was already deregistered; a payload where viewId and id both point at nonexistent records.

Common situations: Stale clients after a workspace reconnect or cloud announcements refresh; announcements with a short validity window; multiple tabs dismissing the same banner; custom code guessing banner ids.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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