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

  1. Set albumThumbnailAssetId to an asset ID that currently belongs to the album
  2. Re-fetch the album's assets and pick a valid one from the list
  3. Omit albumThumbnailAssetId from the update DTO to keep the current thumbnail
  4. 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

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


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)