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 PersistenceException similar 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.

  1. Update your asset move logic for DAM assets.
    Instead of using ResourceResolver.move() for DAM assets, use the AssetManager.moveAsset() API.

  2. 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();
    
  3. 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()
    }
    
  4. Deploy and test the updated workflow in a non-production environment before promoting to production.

  5. Verify that the workflow completes successfully and that assets are moved as expected, without PersistenceException errors.

Note

ResourceResolver.move(sourcePath, targetParentPath) uses the destination parent path, while AssetManager.moveAsset(sourcePath, targetPath) uses the full target asset path.

recommendation-more-help
experience-cloud-kcs-help-kbarticles