pentaho/pentaho-kettle · error · PurgeDeletionException

Must purge files before shared objects

Error message

Must purge files before shared objects

What it means

doDeleteRevisions throws PurgeDeletionException('Must purge files before shared objects') to enforce purge ordering: regular files must be purged before shared-object folders. When the purge specification requests shared-object deletion without having performed/allowed the preceding file purge pass, the service aborts rather than leaving dangling references.

Solutions

  1. Run the purge in two ordered phases: purge regular files first, then shared object folders
  2. Update the PurgeUtilitySpecification so the file-purge phase flag is enabled before the shared-objects phase
  3. Remove the shared-object-only request and purge the parent files together with their shared objects
  4. Check purge logs to confirm the file phase completed before the shared-object phase started

Example fix

// before: shared objects only
purgeSpec.setPath( sharedObjectPath );
service.doDeleteRevisions( purgeSpec );
// after: purge files first, then shared objects
purgeSpec.setPath( filePath );
service.doDeleteRevisions( purgeSpec );
purgeSpec.setPath( sharedObjectPath );
service.doDeleteRevisions( purgeSpec );
Defensive patterns

Strategy: validation

Validate before calling

if ( purgeTargetsSharedObjects( purgeSpec ) && !filesPurgedFirst( purgeSpec ) ) {
  throw new IllegalStateException( "Purge regular files before shared object folders." );
}

Try / catch

try {
  service.doDeleteRevisions( purgeSpec );
} catch ( PurgeDeletionException e ) {
  if ( e.getMessage().contains( "shared objects" ) ) {
    runFilePurgePhase();
    service.doDeleteRevisions( purgeSpec );
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling doDeleteRevisions (via doPurgeUtilSharedObjectsTest-style specifications) where shared object folders are present but the flag/branch indicating files were already purged (the if preceding the else at line 143) is false.

Common situations: Purging only shared objects while regular files still contain revisions referencing them; misconfigured PurgeUtilitySpecification that skips the file-purge phase; users selecting shared-object cleanup expecting it to work standalone.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/433618aac1c76345. Report an issue: GitHub.

Appendix: source

Thrown at plugins/pur/core/src/main/java/com/pentaho/di/purge/UnifiedRepositoryPurgeService.java:143

  public void doDeleteRevisions( PurgeUtilitySpecification purgeSpecification ) throws PurgeDeletionException {
    if ( purgeSpecification != null ) {
      getLogger().setCurrentFilePath( purgeSpecification.getPath() );
      logConfiguration( purgeSpecification );
      if ( purgeSpecification.getPath() != null && !purgeSpecification.getPath().isEmpty() ) {
        processRevisionDeletion( purgeSpecification );
      }

      // Now do shared objects if required
      if ( purgeSpecification.isSharedObjects() ) {
        if ( purgeSpecification.isPurgeFiles() ) {
          for ( String sharedObjectpath : sharedObjectFolders ) {
            purgeSpecification.fileFilter = "*";
            purgeSpecification.setPath( sharedObjectpath );
            processRevisionDeletion( purgeSpecification );
          }
        } else {
          throw new PurgeDeletionException( "Must purge files before shared objects" );
        }
      }
    }
  }

  private void processRevisionDeletion( PurgeUtilitySpecification purgeSpecification ) throws PurgeDeletionException {

    RepositoryRequest repositoryRequest =
        new RepositoryRequest( purgeSpecification.getPath(), true, -1, purgeSpecification.getFileFilter() );
    repositoryRequest.setTypes( FILES_TYPE_FILTER.FILES_FOLDERS );
    repositoryRequest.setIncludeMemberSet( new HashSet<String>( Arrays.asList( new String[] { "name", "id", "folder",
      "path", "versioned", "versionId", "locked" } ) ) );

    getLogger().debug( "Creating file list" );
    RepositoryFileTreeDto tree = getRepoWs().getTreeFromRequest( repositoryRequest );

    processPurgeForTree( tree, purgeSpecification );
  }

View on GitHub (pinned to f3058517a1)