RocketChat/Rocket.Chat · error · Error
invalid view
Error message
invalid view
What it means
`CloudAnnouncementsModule.viewClosed` throws `Error('invalid view')` when neither `payload.payload.view.viewId` nor `payload.payload.view.id` is present. The module accepts both on purpose: `viewId` is preferred, but legacy announcement banners may only carry `id`; when both are absent it cannot resolve which banner to dismiss.
Source
Thrown at apps/meteor/server/modules/core-apps/cloudAnnouncements.module.ts:63
return this.handlePayload(payload);
}
viewSubmit(payload: UiKitCoreAppViewSubmitPayload): Promise<UiKit.ServerInteraction | undefined> {
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';View on GitHub (pinned to b2c16d5842)
Solutions
- Send the announcement banner's identifier as `payload: { view: { viewId: bannerId } }` (use `id` only for legacy banners).
- Re-fetch the current banners for the user and dismiss using the live banner `_id`.
- For programmatic dismissal, call `Banner.dismiss(userId, bannerId)` directly.
Example fix
// before
payload: { view: {}, triggerId }
// after
payload: { view: { viewId: bannerId }, triggerId } Defensive patterns
Strategy: validation
Validate before calling
const { view } = payload.payload ?? {};
if (!view?.viewId && !view?.id) {
throw new Error('view.viewId (or legacy view.id) is required');
}
await announcementsModule.viewClosed(payload); Type guard
const hasViewIdentifier = (
payload: UiKitCoreAppViewClosedPayload,
): payload is UiKitCoreAppViewClosedPayload & { payload: { view: { viewId?: string; id?: string } } } =>
Boolean(payload.payload?.view && (payload.payload.view.viewId || payload.payload.view.id)); Try / catch
try {
await announcementsModule.viewClosed(payload);
} catch (error) {
if (error instanceof Error && error.message === 'invalid view') {
// refetch banners and rebuild the payload with the live banner id
}
} Prevention
- Send viewId; keep view.id only for legacy banners
- Refetch current banners before building close payloads
- Never send an empty view object
When it happens
Trigger: A viewClosed interaction for `cloud-announcements-core` whose `view` object is undefined or contains neither `viewId` nor `id` — e.g. an empty `view: {}`, a malformed modal payload, or fields lost during serialization.
Common situations: Reusing a modal-close payload shape that nests the id elsewhere; test fixtures with only a triggerId; payload transformations that strip the view object.
Related errors
- invalid user
- invalid triggerId
- Invalid user data received from Rocket.Chat Cloud
- invalid user
- invalid user
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/4a1c20cc4e329795.
Report an issue: GitHub.