immich-app/immich · error · BadRequestException

Notification not found

Error message

Notification not found

What it means

NotificationService.get fetches a single notification by id after the access check passes; if notificationRepository.get returns null (no such notification, or it does not belong to a context the user can read), it throws 400 BadRequestException 'Notification not found'. The 400 (rather than 404) is an Immich convention for these lookups.

Source

Thrown at server/src/services/notification.service.ts:58

  }

  async updateAll(auth: AuthDto, dto: NotificationUpdateAllDto) {
    await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NotificationUpdate });
    await this.notificationRepository.updateAll(dto.ids, {
      readAt: dto.readAt,
    });
  }

  async deleteAll(auth: AuthDto, dto: NotificationDeleteAllDto) {
    await this.requireAccess({ auth, ids: dto.ids, permission: Permission.NotificationDelete });
    await this.notificationRepository.deleteAll(dto.ids);
  }

  async get(auth: AuthDto, id: string) {
    await this.requireAccess({ auth, ids: [id], permission: Permission.NotificationRead });
    const item = await this.notificationRepository.get(id);
    if (!item) {
      throw new BadRequestException('Notification not found');
    }
    return mapNotification(item);
  }

  async update(auth: AuthDto, id: string, dto: NotificationUpdateDto) {
    await this.requireAccess({ auth, ids: [id], permission: Permission.NotificationUpdate });
    const item = await this.notificationRepository.update(id, {
      readAt: dto.readAt,
    });
    return mapNotification(item);
  }

  async delete(auth: AuthDto, id: string) {
    await this.requireAccess({ auth, ids: [id], permission: Permission.NotificationDelete });
    await this.notificationRepository.delete(id);
  }

  @OnJob({ name: JobName.NotificationsCleanup, queue: QueueName.BackgroundTask })

View on GitHub (pinned to 199723261c)

Solutions

  1. Refresh the notification list (GET /notifications) and operate only on current ids.
  2. Handle the 400 gracefully in the client and drop the stale reference.
  3. Confirm the id is a valid UUID.

Example fix

// before
api.notifications.get('stale-id');
// after
const list = await api.notifications.list();
await api.notifications.get(list[0].id);
Defensive patterns

Strategy: validation

Validate before calling

const list = await api.notificationApi.list();
if (!list.some((n) => n.id === id)) {
  throw new Error(`Notification ${id} not found`);
}
await api.notificationApi.get(id);

Type guard

const notificationExists = (id: string, items: { id: string }[]) =>
  items.some((n) => n.id === id);

Try / catch

try {
  return await api.notificationApi.get(id);
} catch (e) {
  if (e.status === 400 && /Notification not found/.test(e.message)) {
    // refresh the list and drop the stale id
  } else throw e;
}

Prevention

When it happens

Trigger: GET /notifications/{id} for an id that does not exist or that the access scope does not cover. Common with a stale notification id in the UI after it was deleted, or a hand-crafted request.

Common situations: User dismissed/deleted a notification but the client still references it; wrong id copied; another admin cleared all notifications.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/455ab1b82d515ae9. Report an issue: GitHub.