immich-app/immich · error · BadRequestException
Invalid shared link type
Error message
Invalid shared link type
What it means
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.
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.
Example fix
// before
await api.addSharedLinkAssets(auth, albumLinkId, { assetIds });
// after
const link = await api.getSharedLink(auth, albumLinkId);
if (link.type === 'Individual') {
await api.addSharedLinkAssets(auth, albumLinkId, { assetIds });
} Defensive patterns
Strategy: validation
Validate before calling
const link = await api.getSharedLink(auth, id);
if (link.type !== 'Individual') throw new Error(`Link ${id} is not individual; cannot add assets`); Type guard
const isIndividualLink = (link: { type: string }): link is { type: 'Individual' } => link.type === 'Individual'; Try / catch
try {
await api.addSharedLinkAssets(auth, id, { assetIds });
} catch (e) {
if (e.status === 400 && /Invalid shared link type/.test(e.message)) {
// fall back to album-based update or surface a user-facing hint
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Primary asset must be in the stack
- Asset dimensions are not available for editing
- Asset not in stack
- assetIds, albumId, or userId is required
- At least two people are required for merging
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/76602efdc529a7fb.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/shared-link.service.ts:153
} catch (error) {
this.handleError(error);
}
}
async remove(auth: AuthDto, id: string): Promise<void> {
const sharedLink = await this.findOrFail(auth.user.id, id);
await this.sharedLinkRepository.remove(sharedLink.id);
}
// TODO: replace `userId` with permissions and access control checks
private findOrFail(userId: string, id: string) {
return findOrFail(() => this.sharedLinkRepository.get(userId, id), 'Shared link');
}
async addAssets(auth: AuthDto, id: string, dto: AssetIdsDto): Promise<AssetIdsResponseDto[]> {
const sharedLink = await this.findOrFail(auth.user.id, id);
if (sharedLink.type !== SharedLinkType.Individual) {
throw new BadRequestException('Invalid shared link type');
}
const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
const allowedAssetIds = await this.checkAccess({
auth,
permission: Permission.AssetShare,
ids: notPresentAssetIds,
});
const results: AssetIdsResponseDto[] = [];
for (const assetId of dto.assetIds) {
const hasAsset = existingAssetIds.has(assetId);
if (hasAsset) {
results.push({ assetId, success: false, error: AssetIdErrorReason.DUPLICATE });
continue;
}
View on GitHub (pinned to e55ac299a4)