immich-app/immich · warning · BadRequestException

Invalid assetIds

Error message

Invalid assetIds

What it means

In SharedLinkService.create(), for SharedLinkType.Individual the service requires dto.assetIds to be a non-empty array; otherwise it throws BadRequestException 'Invalid assetIds' (shared-link.service.ts:81, HTTP 400) before access checks.

Source

Thrown at server/src/services/shared-link.service.ts:81

  async get(auth: AuthDto, id: string): Promise<SharedLinkResponseDto> {
    const sharedLink = await this.findOrFail(auth.user.id, id);
    return mapSharedLink(sharedLink, { stripAssetMetadata: false });
  }

  async create(auth: AuthDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> {
    switch (dto.type) {
      case SharedLinkType.Album: {
        if (!dto.albumId) {
          throw new BadRequestException('Invalid albumId');
        }
        await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [dto.albumId] });
        break;
      }

      case SharedLinkType.Individual: {
        if (!dto.assetIds || dto.assetIds.length === 0) {
          throw new BadRequestException('Invalid assetIds');
        }

        await this.requireAccess({ auth, permission: Permission.AssetShare, ids: dto.assetIds });

        break;
      }
    }

    try {
      const sharedLink = await this.sharedLinkRepository.create({
        key: this.cryptoRepository.randomBytes(50),
        userId: auth.user.id,
        type: dto.type,
        albumId: dto.albumId || null,
        assetIds: dto.assetIds,
        description: dto.description || null,
        password: dto.password,
        expiresAt: dto.expiresAt || null,

View on GitHub (pinned to 199723261c)

Solutions

  1. Provide at least one asset id in dto.assetIds when type is Individual.
  2. Disable the share action until at least one asset is selected.
  3. Validate the array is non-empty on the client before submitting.

Example fix

// before
await create({ type: SharedLinkType.Individual, assetIds: [] });
// after
await create({ type: SharedLinkType.Individual, assetIds: [assetId] });
Defensive patterns

Strategy: validation

Validate before calling

if (dto.type === SharedLinkType.Individual && (!dto.assetIds || dto.assetIds.length === 0)) {
  throw new Error('At least one assetId is required for individual shared links.');
}
await sharedLinkApi.create(dto);

Type guard

const hasAssetIds = (dto: SharedLinkCreateDto): boolean =>
  dto.type !== SharedLinkType.Individual || (Array.isArray(dto.assetIds) && dto.assetIds.length > 0);

Try / catch

try {
  await sharedLinkApi.create(dto);
} catch (e) {
  if (e instanceof BadRequestException && /assetIds/i.test(e.message)) {
    promptAssetSelection();
  } else throw e;
}

Prevention

When it happens

Trigger: POST /shared-links with type=Individual but assetIds missing, null, or an empty array. An individual shared link must reference at least one asset.

Common situations: Creating a share from an empty selection, the selection state being cleared before the create call, or a bulk action invoked with no assets checked.

Related errors


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