immich-app/immich · error · Error
errorMessage || error
Error message
errorMessage || error
What it means
Thrown by the duplicates resolution UI (Svelte route +page.svelte) when the server's resolveDuplicates call returns success=false for a group. The message is errorMessage || error from the response, so it forwards whatever the backend reported. This is a client-side re-throw to trigger the surrounding error/notification handler.
Source
Thrown at web/src/routes/(user)/utilities/duplicates/[[photos=photos]]/[[assetId=id]]/+page.svelte:114
};
const handleResolve = async (duplicateId: string, duplicateAssetIds: string[], trashIds: string[]) => {
const forceDelete = !featureFlagsManager.value.trash;
const shouldConfirmDelete = trashIds.length > 0 && forceDelete;
return withConfirmation(
async () => {
const keepAssetIds = duplicateAssetIds.filter((id) => !trashIds.includes(id));
const response = await resolveDuplicates({
duplicateResolveDto: {
groups: [{ duplicateId, keepAssetIds, trashAssetIds: trashIds }],
},
});
const { success, error, errorMessage } = response[0];
if (!success) {
throw new Error(errorMessage || error);
}
duplicates = duplicates.filter((duplicate) => duplicate.duplicateId !== duplicateId);
deletedNotification(trashIds.length);
await navigateToIndex(duplicatesIndex);
},
shouldConfirmDelete ? $t('delete_duplicates_confirmation') : undefined,
shouldConfirmDelete ? $t('permanently_delete') : undefined,
);
};
const handleStack = async (duplicateId: string, assets: AssetResponseDto[]) => {
const assetIds = assets.map((asset) => asset.id);
await createStack({ stackCreateDto: { assetIds } });
await updateAssets({ assetBulkUpdateDto: { ids: assetIds, duplicateId: null } });
duplicates = duplicates.filter((duplicate) => duplicate.duplicateId !== duplicateId);
await navigateToIndex(duplicatesIndex);View on GitHub (pinned to 199723261c)
Solutions
- Refresh the duplicates list and retry — the group may have changed.
- Inspect the response.error/errorMessage in DevTools for the specific backend reason.
- Re-run the duplicate detection job if the list is stale/inconsistent.
- Ensure the user has AssetUpdate/AssetDelete permission on all assets in the group.
Example fix
// before
const { success, error, errorMessage } = response[0];
if (!success) throw new Error(errorMessage || error);
// after — clearer user-facing message
if (!success) throw new Error(errorMessage || error || $t('errors.unable_to_resolve_duplicates')); Defensive patterns
Strategy: try-catch
Validate before calling
// re-fetch the group to ensure assets still exist before resolving
const group = await getDuplicateGroup(duplicateId);
if (!group || group.assetIds.some(id => !knownAssets.has(id))) { refreshDuplicates(); return; } Type guard
const isResolveSuccess = (r: { success?: boolean }): boolean => Boolean(r?.success); Try / catch
try { await resolveDuplicates({ duplicateResolveDto }); }
catch (e) { if (/duplicate/i.test(e.message)) { await refreshDuplicates(); } else throw e; } Prevention
- Refresh the duplicates list before resolving if it has been open a while.
- Show the backend errorMessage to the user for actionable guidance.
When it happens
Trigger: User clicks resolve-keep/trash in the Duplicates utility; the backend resolveDuplicates RPC returns { success: false, error/errorMessage } for the duplicateId group (e.g. assets no longer present, permission denied, concurrent modification).
Common situations: Assets in the duplicate group were deleted or moved between the duplicate-scan job and the user resolving them; permission changes; another client resolved the same group first; backend validation failure.
Related errors
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/20710f274697ae27.
Report an issue: GitHub.