pentaho/pentaho-kettle · error · KettleException
Unable to complete revision deletion
Error message
Unable to complete revision deletion
What it means
UnifiedRepositoryPurgeService.processDeleteException is the single wrapping point for any Throwable raised while deleting repository revisions. It rethrows the original cause (e.g. a repository connectivity or permission error) inside a KettleException with the generic message 'Unable to complete revision deletion', so the real reason is always in getCause().
Solutions
- Inspect the wrapped cause via exception.getCause() to find the actual repository failure
- Verify repository connectivity and re-run the purge after transient network issues clear
- Check that the executing user has delete permissions on all target revisions in the target path
- Re-run deleteVersionsBeforeDate with a narrower date/path to isolate the failing file
Example fix
// before: blanket wrap hides root cause handling
try { ...delete revisions... } catch ( Throwable e ) { processDeleteException( e ); }
// after: handle known recoverable causes before wrapping
catch ( Throwable e ) {
if ( e instanceof java.net.ConnectException ) { retryWithBackoff(); } else { processDeleteException( e ); }
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify repository reachable before purge
if ( !repoConnectivityCheck( repositoryUrl ) ) { throw new IllegalStateException( "Repository unreachable; aborting purge" ); } Try / catch
try {
service.deleteVersionsBeforeDate( purgeSpec, cutoffDate );
} catch ( KettleException e ) {
Throwable root = e.getCause();
log.error( "Revision deletion failed due to: " + root.getMessage(), root );
if ( isTransient( root ) ) { scheduleRetry(); } else { throw new PurgeFailure( root ); }
} Prevention
- Always log/inspect getCause() rather than the generic message
- Check repository connectivity and permissions before large purges
- Run purges during low-traffic windows to reduce lock contention
- Scope purges to narrow paths/dates so failures are isolatable
When it happens
Trigger: deleteVersionsBeforeDate() performs revision deletion per file/version; any exception thrown by the underlying repository delete calls is passed to processDeleteException and wrapped in a KettleException.
Common situations: Repository outages or dropped connections mid-purge; insufficient permissions to delete specific revisions; files locked or referenced by shared objects; concurrent purge runs causing optimistic-lock failures.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- RepositoryExportDialog.Error.CreateUpdate
- AbsSecurityManager.ERROR_0004_UNABLE_TO_APPLY_LOGICAL_ROLES_TO_RUNTIME_ROLE
- AbsSecurityProvider.ERROR_0002_UNABLE_TO_ACCESS_IS_ALLOWED
- AccessInputMeta.Exception.ErrorReadingRepository
- AddSequenceMeta.Exception.UnableToReadStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/fe22466d96650184.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/pur/core/src/main/java/com/pentaho/di/purge/UnifiedRepositoryPurgeService.java:123
int i = 0;
int listSize = versionList.size();
if ( listSize > versionCount ) {
getLogger().info( "version count: removing versions" );
}
for ( VersionSummary versionSummary : versionList ) {
if ( i < listSize - versionCount ) {
Serializable versionId = versionSummary.getId();
getLogger().debug( "removing version " + versionId.toString() );
unifiedRepository.deleteFileAtVersion( fileId, versionId );
i++;
} else {
break;
}
}
}
private void processDeleteException( Throwable e ) throws KettleException {
throw new KettleException( "Unable to complete revision deletion", e );
}
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 );
}View on GitHub (pinned to f3058517a1)