{"record":{"id":"c658a45b75c50202","repo":"immich-app/immich","slug":"invalid-albumid","errorCode":null,"errorMessage":"Invalid albumId","messagePattern":"Invalid albumId","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","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/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/shared-link.service.ts#L55-L91","documentation":"In SharedLinkService.create(), for SharedLinkType.Album the service requires dto.albumId to be present; a missing/empty albumId throws BadRequestException 'Invalid albumId' (shared-link.service.ts:73, HTTP 400) before any access check.","triggerScenarios":"POST /shared-links with type=Album but albumId omitted/null/empty string. Creating an album shared link requires identifying which album to share.","commonSituations":"Client building the DTO from an album list selection where the user hadn't picked an album, or a refactor that dropped the albumId field.","solutions":["Set dto.albumId to a valid existing album UUID when type is Album.","On the client, disable the 'create shared link' action until an album is selected.","Validate the DTO shape before sending."],"exampleFix":"// before\nawait create({ type: SharedLinkType.Album });\n// after\nawait create({ type: SharedLinkType.Album, albumId });","handlingStrategy":"validation","validationCode":"if (dto.type === SharedLinkType.Album && !dto.albumId) {\n  throw new Error('albumId is required for album shared links.');\n}\nawait sharedLinkApi.create(dto);","typeGuard":"const isValidAlbumCreate = (dto: SharedLinkCreateDto): boolean =>\n  dto.type !== SharedLinkType.Album || !!dto.albumId;","tryCatchPattern":"try {\n  await sharedLinkApi.create(dto);\n} catch (e) {\n  if (e instanceof BadRequestException && /albumId/i.test(e.message)) {\n    promptAlbumSelection();\n  } else throw e;\n}","preventionTips":["Disable the create-album-share action until an album is selected.","Build the DTO from the current selection state, not stale values."],"tags":["shared-link","album","validation","dto"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}