{"record":{"id":"c658a45b75c50202","repo":"immich-app/immich","slug":"invalid-albumid","errorCode":null,"errorMessage":"Invalid albumId","messagePattern":"Invalid albumId","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/shared-link.service.ts","lineNumber":73,"sourceCode":"    const { id, password } = sharedLink;\n\n    if (password && !authTokens.includes(this.asToken({ id, password }))) {\n      throw new UnauthorizedException('Password required');\n    }\n\n    return mapSharedLink(sharedLink, { stripAssetMetadata: !sharedLink.showExif });\n  }\n\n  async get(auth: AuthDto, id: string): Promise<SharedLinkResponseDto> {\n    const sharedLink = await this.findOrFail(auth.user.id, id);\n    return mapSharedLink(sharedLink, { stripAssetMetadata: false });\n  }\n\n  async create(auth: AuthDto, dto: SharedLinkCreateDto): Promise<SharedLinkResponseDto> {\n    switch (dto.type) {\n      case SharedLinkType.Album: {\n        if (!dto.albumId) {\n          throw new BadRequestException('Invalid albumId');\n        }\n        await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [dto.albumId] });\n        break;\n      }\n\n      case SharedLinkType.Individual: {\n        if (!dto.assetIds || dto.assetIds.length === 0) {\n          throw new BadRequestException('Invalid assetIds');\n        }\n\n        await this.requireAccess({ auth, permission: Permission.AssetShare, ids: dto.assetIds });\n\n        break;\n      }\n    }\n\n    try {\n      const sharedLink = await this.sharedLinkRepository.create({","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/shared-link.service.ts#L55-L91","documentation":"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.","triggerScenarios":"POST /shared-links with dto.type = SharedLinkType.Album but dto.albumId null/undefined/empty.","commonSituations":"Client builds the create payload generically and forgets albumId; album selection cleared in UI before submit; type set to Album programmatically without payload.","solutions":["Pass dto.albumId set to the album to share when type is Album.","If sharing individual assets instead, set type to Individual and supply assetIds.","Validate client-side that albumId is a non-empty string for Album links."],"exampleFix":"// before\nawait api.createSharedLink({ type: SharedLinkType.Album });\n// after\nawait api.createSharedLink({ type: SharedLinkType.Album, albumId: album.id });","handlingStrategy":"validation","validationCode":"if (dto.type === SharedLinkType.Album && !dto.albumId) {\n  throw new Error('Album shared links require albumId');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.createSharedLink(dto);\n} catch (e) {\n  if (e.status === 400 && e.message === 'Invalid albumId') {\n    // surface 'select an album' to the user\n  } else {\n    throw e;\n  }\n}","preventionTips":["Require album selection in the UI when Album type is chosen.","Validate payloads against the DTO before sending.","Fall back to Individual type only with a non-empty asset list."],"tags":["shared-link","validation","missing-field","album"],"backgroundTag":"missing-required-argument","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}