immich-app/immich · error · BadRequestException

Invalid albumId

Error message

Invalid albumId

What it means

SharedLink.create validates per-type required payloads. For SharedLinkType.Album an albumId is mandatory; if absent the link could not reference any album, so the service throws BadRequestException('Invalid albumId') before the share-permission check.

Solutions

  1. Pass dto.albumId set to the album to share when type is Album.
  2. If sharing individual assets instead, set type to Individual and supply assetIds.
  3. Validate client-side that albumId is a non-empty string for Album links.

Example fix

// before
await api.createSharedLink({ type: SharedLinkType.Album });
// after
await api.createSharedLink({ type: SharedLinkType.Album, albumId: album.id });
Defensive patterns

Strategy: validation

Validate before calling

if (dto.type === SharedLinkType.Album && !dto.albumId) {
  throw new Error('Album shared links require albumId');
}

Try / catch

try {
  await api.createSharedLink(dto);
} catch (e) {
  if (e.status === 400 && e.message === 'Invalid albumId') {
    // surface 'select an album' to the user
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: POST /shared-links with dto.type = SharedLinkType.Album but dto.albumId null/undefined/empty.

Common situations: Client builds the create payload generically and forgets albumId; album selection cleared in UI before submit; type set to Album programmatically without payload.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — 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/c658a45b75c50202. Report an issue: GitHub.

Appendix: source

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

    const { id, password } = sharedLink;

    if (password && !authTokens.includes(this.asToken({ id, password }))) {
      throw new UnauthorizedException('Password required');
    }

    return mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif });
  }

  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({

View on GitHub (pinned to e55ac299a4)