AEM asset promotion workflow fails with PersistenceException on jcr:uuid when using ResourceResolver.move()
This article explains how to resolve workflow failures in Adobe Experience Manager (AEM) when moving DAM assets using ResourceResolver.move(), which can result in a PersistenceException related to the protected jcr:uuid property. The recommended solution is to use the DAM-specific AssetManager.moveAsset() API.
Description description
Environment
Adobe Experience Manager (AEM) environments where custom workflows move DAM assets programmatically.
Issue/Symptoms
- Asset promotion workflows that move assets within the DAM using
ResourceResolver.move()fail. - The workflow may appear completed in the user interface (UI), but backend logs show a
PersistenceExceptionsimilar to the following:
org.apache.sling.api.resource.PersistenceException: Value of class 'class java.lang.String' for property 'jcr:uuid' can't be put into node '[ asset path] '.
- All affected assets are under
/content/dam/.
Cause
The generic ResourceResolver.move() method attempts to copy protected properties such as jcr:uuid, which is not permitted by the JCR repository. The DAM-specific AssetManager.moveAsset() API handles asset moves correctly without attempting to set protected properties.
`
Resolution resolution
Follow the steps below to resolve the issue.
-
Update your asset move logic for DAM assets.
Instead of usingResourceResolver.move()for DAM assets, use theAssetManager.moveAsset()API. -
Replace the existing implementation with the recommended implementation.
Current code:code language-none createFolderIfNotExists(resourceResolver, sourcePath, targetParentPath); resourceResolver.move(sourcePath, targetParentPath); resourceResolver.commit();Recommended code for DAM assets:
code language-none createFolderIfNotExists(resourceResolver, sourcePath, targetParentPath); AssetManager assetManager = resourceResolver.adaptTo(AssetManager.class); if (assetManager == null) { throw new PersistenceException("Unable to adapt ResourceResolver to AssetManager"); } assetManager.moveAsset(sourcePath, targetPath); resourceResolver.commit(); -
For workflows that handle both DAM and non-DAM content, use conditional logic:
code language-none Resource sourceResource = resourceResolver.getResource(sourcePath); if (sourceResource != null && "dam:Asset".equals(sourceResource.getResourceType())) { // Use AssetManager.moveAsset() } else { // Use ResourceResolver.move() } -
Deploy and test the updated workflow in a non-production environment before promoting to production.
-
Verify that the workflow completes successfully and that assets are moved as expected, without
PersistenceExceptionerrors.
Note
ResourceResolver.move(sourcePath, targetParentPath) uses the destination parent path, while AssetManager.moveAsset(sourcePath, targetPath) uses the full target asset path.