{"record":{"id":"76602efdc529a7fb","repo":"immich-app/immich","slug":"invalid-shared-link-type","errorCode":null,"errorMessage":"Invalid shared link type","messagePattern":"Invalid shared link type","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/shared-link.service.ts","lineNumber":153,"sourceCode":"    } catch (error) {\n      this.handleError(error);\n    }\n  }\n\n  async remove(auth: AuthDto, id: string): Promise<void> {\n    const sharedLink = await this.findOrFail(auth.user.id, id);\n    await this.sharedLinkRepository.remove(sharedLink.id);\n  }\n\n  // TODO: replace `userId` with permissions and access control checks\n  private findOrFail(userId: string, id: string) {\n    return findOrFail(() => this.sharedLinkRepository.get(userId, id), 'Shared link');\n  }\n\n  async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {\n    const sharedLink = await this.findOrFail(auth.user.id, id);\n    if (sharedLink.type !== SharedLinkType.Individual) {\n      throw new BadRequestException('Invalid shared link type');\n    }\n\n    const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));\n    const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));\n    const allowedAssetIds = await this.checkAccess({\n      auth,\n      permission: Permission.AssetShare,\n      ids: notPresentAssetIds,\n    });\n\n    const results: AssetIdsResponseDto[] = [];\n    for (const assetId of dto.assetIds) {\n      const hasAsset = existingAssetIds.has(assetId);\n      if (hasAsset) {\n        results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE });\n        continue;\n      }\n","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/shared-link.service.ts#L135-L171","documentation":"Immich throws this BadRequestException when a client tries to add assets to a shared link whose type is not 'Individual'. Only individual shared links allow direct asset management; album shared links derive their assets from the linked album, so mutating their asset list is invalid.","triggerScenarios":"Calling the addAssets endpoint (POST /shared-links/:id/assets) with the id of an album-type (non-Individual) shared link, e.g. a link created for a whole album.","commonSituations":"Scripts or mobile clients iterating over shared links and applying asset updates to all of them; a link's type changed semantics between API versions; the client cached the wrong link id.","solutions":["Verify the shared link's type is SharedLinkType.Individual before calling addAssets (GET /shared-links or check the link object).","If the goal is to change album-link contents, update the linked album's assets instead of the shared link.","Create a new Individual shared link containing the desired assets if direct asset control is needed.","Handle 400 responses by checking the link type from the API response before retrying."],"exampleFix":"// before\nawait api.addSharedLinkAssets(auth, albumLinkId, { assetIds });\n// after\nconst link = await api.getSharedLink(auth, albumLinkId);\nif (link.type === 'Individual') {\n  await api.addSharedLinkAssets(auth, albumLinkId, { assetIds });\n}","handlingStrategy":"validation","validationCode":"const link = await api.getSharedLink(auth, id);\nif (link.type !== 'Individual') throw new Error(`Link ${id} is not individual; cannot add assets`);","typeGuard":"const isIndividualLink = (link: { type: string }): link is { type: 'Individual' } => link.type === 'Individual';","tryCatchPattern":"try {\n  await api.addSharedLinkAssets(auth, id, { assetIds });\n} catch (e) {\n  if (e.status === 400 && /Invalid shared link type/.test(e.message)) {\n    // fall back to album-based update or surface a user-facing hint\n  } else throw e;\n}","preventionTips":["Fetch the shared link and check type before mutating its assets","Only send asset-management calls for individually created links","Handle 400 responses by branching on link.type","Keep client models in sync with the SharedLinkType enum"],"tags":["immich","shared-links","bad-request","validation"],"backgroundTag":"invalid-enum-value","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"}