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
- Re-fetch the current banners for the user and dismiss using the live banner `_id`.
- Verify existence before dismissing: `await Banners.findByIds([viewId, id].filter(Boolean)).toArray()`.
- 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
- Use banner ids taken from the current Banners collection, not cached ones
- Refetch announcements after workspace/cloud syncs
- Treat duplicate dismissals of the same banner as no-ops
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
- invalid user
- invalid view
- invalid triggerId
- Invalid user data received from Rocket.Chat Cloud
- Mention bot - Failed to retrieve message information
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/8a44abf807b3b4c3.
Report an issue: GitHub.