immich-app/immich · error · BadRequestException
Invalid album thumbnail
Error message
Invalid album thumbnail
What it means
When updating an album's thumbnail, the service verifies the candidate asset actually belongs to the album via albumRepository.getAssetIds; if the asset is not part of the album, it throws 'Invalid album thumbnail'.
Solutions
- Set albumThumbnailAssetId to an asset ID that currently belongs to the album
- Re-fetch the album's assets and pick a valid one from the list
- Omit albumThumbnailAssetId from the update DTO to keep the current thumbnail
- On the client, handle the 400 by clearing the stale thumbnail reference
Example fix
// before
await api.updateAlbum(id, { albumThumbnailAssetId: staleAssetId });
// after
const album = await api.getAlbumInfo(id);
const validId = album.assets.find(a => a.id === staleAssetId)?.id ?? album.albumThumbnailAssetId;
await api.updateAlbum(id, { albumThumbnailAssetId: validId }); Defensive patterns
Strategy: validation
Validate before calling
const assetIds = new Set((await api.getAlbumInfo(albumId)).assets.map(a => a.id));
if (!assetIds.has(thumbnailAssetId)) throw new Error('thumbnail asset not in album'); Type guard
function isInAlbum(assetId: string, album: { assets: { id: string }[] }): boolean {
return album.assets.some(a => a.id === assetId);
} Try / catch
try {
await api.updateAlbum(id, { albumThumbnailAssetId });
} catch (e) {
if ((e as Error).message === 'Invalid album thumbnail') {
return api.updateAlbum(id, {}); // keep current thumbnail
}
throw e;
} Prevention
- Pick thumbnails only from the album's current asset list
- Clear cached thumbnail IDs when assets are removed from the album
- Omit albumThumbnailAssetId when unchanged
- Re-fetch album info before updates instead of trusting local state
When it happens
Trigger: PATCH /albums/:id with albumThumbnailAssetId set to an asset ID that is not in the album (deleted asset, wrong album, or never-added asset).
Common situations: Clients caching an old thumbnail asset ID after the asset was removed from the album, or UI state pointing at an asset from a different album.
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
- Cannot add another owner
- Invalid albumId
- Invalid user
- Album not shared with user
- Asset dimensions are not available for editing
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/e55fb0da0cf0d830.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/album.service.ts:148
auth.user.id,
);
for (const { userId } of albumUsers) {
await this.eventRepository.emit('AlbumInvite', { id: album.id, userId, senderName: auth.user.name });
}
return mapAlbum(album);
}
async update(auth: AuthDto, id: string, dto: UpdateAlbumDto): Promise<AlbumResponseDto> {
await this.requireAccess({ auth, permission: Permission.AlbumUpdate, ids: [id] });
const album = await this.findOrFail(id, auth.user.id, { withAssets: true });
if (dto.albumThumbnailAssetId) {
const results = await this.albumRepository.getAssetIds(id, [dto.albumThumbnailAssetId]);
if (results.size === 0) {
throw new BadRequestException('Invalid album thumbnail');
}
}
const updatedAlbum = await this.albumRepository.update(
album.id,
{
id: album.id,
albumName: dto.albumName,
description: dto.description,
albumThumbnailAssetId: dto.albumThumbnailAssetId,
isActivityEnabled: dto.isActivityEnabled,
order: dto.order,
},
auth.user.id,
);
return mapAlbum({ ...updatedAlbum, assets: album.assets });
}
View on GitHub (pinned to e55ac299a4)