immich-app/immich · warning · BadRequestException
Notification not found
Error message
Notification not found
What it means
Thrown by NotificationService.get when no notification exists for the given id, after access control passes. The repository returns undefined and the service raises a BadRequestException instead of returning a null/mapped object. In practice this means the notification id is unknown, stale, or already deleted.
Solutions
- Verify the notification id is correct and still exists (e.g. re-fetch the notification list)
- Refresh the client's notification list to drop stale ids before retrying
- Handle 400 'Notification not found' on the client by removing the item from local state
- If ids come from another environment, use the correct instance's ids
Example fix
// before
const n = await api.notificationApi.getNotificationById(staleId); // throws 400
// after
const list = await api.notificationApi.getNotifications();
const n = list.find(x => x.id === staleId);
if (n) { /* use it */ } else { removeFromLocalState(staleId); } Defensive patterns
Strategy: validation
Validate before calling
const list = await api.notificationApi.getNotifications();
if (!list.some(n => n.id === id)) {
throw new Error(`Notification ${id} no longer exists; refresh list`);
} Type guard
function isKnownNotification(id, notifications) {
return typeof id === 'string' && id.length > 0 &&
notifications.some(n => n && typeof n.id === 'string' && n.id === id);
} Try / catch
try {
const n = await api.notificationApi.getNotificationById(id);
return n;
} catch (e) {
if (String(e.message).includes('Notification not found')) {
removeFromLocalState(id); // drop stale id, optionally refetch list
return null;
}
throw e;
} Prevention
- Refresh the notification list after delete/dismiss operations
- Never reuse ids across environments or instances
- Treat 400 'Notification not found' as a stale-cache signal, not a bug
- Remove consumed notifications from client state immediately
When it happens
Trigger: GET /notifications/:id with an id that does not exist in the database, was already dismissed/deleted, or is malformed/points to another instance's data.
Common situations: Client caches or bookmarks a notification id that has since been removed; UI re-loads a stale list after cleanup job deleted rows; copy-pasted id from another environment; double-click causing the first request to delete/consume the notification.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Metadata with key " " not found for asset with id
- Asset media not found
- Asset metadata is not yet ready for streaming
- Asset not found
- Asset not found
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/455ab1b82d515ae9.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)