{"record":{"id":"af69042f5bcaed61","repo":"immich-app/immich","slug":"invalid-assetids","errorCode":null,"errorMessage":"Invalid assetIds","messagePattern":"Invalid assetIds","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/shared-link.service.ts","lineNumber":81,"sourceCode":"\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({\n        key: this.cryptoRepository.randomBytes(50),\n        userId: auth.user.id,\n        type: dto.type,\n        albumId: dto.albumId || null,\n        assetIds: dto.assetIds,\n        description: dto.description || null,\n        password: dto.password,\n        expiresAt: dto.expiresAt || null,","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/shared-link.service.ts#L63-L99","documentation":"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.","triggerScenarios":"POST /shared-links with type=Individual but assetIds missing, null, or an empty array. An individual shared link must reference at least one asset.","commonSituations":"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.","solutions":["Provide at least one asset id in dto.assetIds when type is Individual.","Disable the share action until at least one asset is selected.","Validate the array is non-empty on the client before submitting."],"exampleFix":"// before\nawait create({ type: SharedLinkType.Individual, assetIds: [] });\n// after\nawait create({ type: SharedLinkType.Individual, assetIds: [assetId] });","handlingStrategy":"validation","validationCode":"if (dto.type === SharedLinkType.Individual && (!dto.assetIds || dto.assetIds.length === 0)) {\n  throw new Error('At least one assetId is required for individual shared links.');\n}\nawait sharedLinkApi.create(dto);","typeGuard":"const hasAssetIds = (dto: SharedLinkCreateDto): boolean =>\n  dto.type !== SharedLinkType.Individual || (Array.isArray(dto.assetIds) && dto.assetIds.length > 0);","tryCatchPattern":"try {\n  await sharedLinkApi.create(dto);\n} catch (e) {\n  if (e instanceof BadRequestException && /assetIds/i.test(e.message)) {\n    promptAssetSelection();\n  } else throw e;\n}","preventionTips":["Require a non-empty asset selection before enabling the share action.","Validate the DTO array length client-side."],"tags":["shared-link","asset","validation","dto"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}