{"record":{"id":"433618aac1c76345","repo":"pentaho/pentaho-kettle","slug":"must-purge-files-before-shared-objects","errorCode":null,"errorMessage":"Must purge files before shared objects","messagePattern":"Must purge files before shared objects","errorType":"exception","errorClass":"PurgeDeletionException","httpStatus":null,"severity":"error","filePath":"plugins/pur/core/src/main/java/com/pentaho/di/purge/UnifiedRepositoryPurgeService.java","lineNumber":143,"sourceCode":"\n  public void doDeleteRevisions( PurgeUtilitySpecification purgeSpecification ) throws PurgeDeletionException {\n    if ( purgeSpecification != null ) {\n      getLogger().setCurrentFilePath( purgeSpecification.getPath() );\n      logConfiguration( purgeSpecification );\n      if ( purgeSpecification.getPath() != null && !purgeSpecification.getPath().isEmpty() ) {\n        processRevisionDeletion( purgeSpecification );\n      }\n\n      // Now do shared objects if required\n      if ( purgeSpecification.isSharedObjects() ) {\n        if ( purgeSpecification.isPurgeFiles() ) {\n          for ( String sharedObjectpath : sharedObjectFolders ) {\n            purgeSpecification.fileFilter = \"*\";\n            purgeSpecification.setPath( sharedObjectpath );\n            processRevisionDeletion( purgeSpecification );\n          }\n        } else {\n          throw new PurgeDeletionException( \"Must purge files before shared objects\" );\n        }\n      }\n    }\n  }\n\n  private void processRevisionDeletion( PurgeUtilitySpecification purgeSpecification ) throws PurgeDeletionException {\n\n    RepositoryRequest repositoryRequest =\n        new RepositoryRequest( purgeSpecification.getPath(), true, -1, purgeSpecification.getFileFilter() );\n    repositoryRequest.setTypes( FILES_TYPE_FILTER.FILES_FOLDERS );\n    repositoryRequest.setIncludeMemberSet( new HashSet<String>( Arrays.asList( new String[] { \"name\", \"id\", \"folder\",\n      \"path\", \"versioned\", \"versionId\", \"locked\" } ) ) );\n\n    getLogger().debug( \"Creating file list\" );\n    RepositoryFileTreeDto tree = getRepoWs().getTreeFromRequest( repositoryRequest );\n\n    processPurgeForTree( tree, purgeSpecification );\n  }","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/pur/core/src/main/java/com/pentaho/di/purge/UnifiedRepositoryPurgeService.java#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the purge in two ordered phases: purge regular files first, then shared object folders","Update the PurgeUtilitySpecification so the file-purge phase flag is enabled before the shared-objects phase","Remove the shared-object-only request and purge the parent files together with their shared objects","Check purge logs to confirm the file phase completed before the shared-object phase started"],"exampleFix":"// before: shared objects only\npurgeSpec.setPath( sharedObjectPath );\nservice.doDeleteRevisions( purgeSpec );\n// after: purge files first, then shared objects\npurgeSpec.setPath( filePath );\nservice.doDeleteRevisions( purgeSpec );\npurgeSpec.setPath( sharedObjectPath );\nservice.doDeleteRevisions( purgeSpec );","handlingStrategy":"validation","validationCode":"if ( purgeTargetsSharedObjects( purgeSpec ) && !filesPurgedFirst( purgeSpec ) ) {\n  throw new IllegalStateException( \"Purge regular files before shared object folders.\" );\n}","typeGuard":null,"tryCatchPattern":"try {\n  service.doDeleteRevisions( purgeSpec );\n} catch ( PurgeDeletionException e ) {\n  if ( e.getMessage().contains( \"shared objects\" ) ) {\n    runFilePurgePhase();\n    service.doDeleteRevisions( purgeSpec );\n  } else { throw e; }\n}","preventionTips":["Always execute the file purge phase before the shared-objects phase","Encode the two-phase order in your purge driver code","Review PurgeUtilitySpecification flags to ensure the file phase isn't skipped","Test purge flows on a sandbox repository before production"],"tags":["ordering","repository","shared-objects","purge"],"backgroundTag":"invalid-state-transition","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}