pentaho/pentaho-kettle · error · KettleException

File is in the Trash. Use Save As.

Error message

File is in the Trash. Use Save As.

What it means

Before saving, PurRepository checks (when checkDeleted is enabled) whether the target file currently lives in the repository Trash. A deleted file cannot be updated in place, so the save is aborted and the developer is told to use Save As. The message deliberately references UI behavior.

Solutions

  1. Use 'Save As' to save the element to a new file/path
  2. Restore the file from the repository Trash, then retry the save
  3. Re-create the file at the original location and then save over it
Defensive patterns

Strategy: validation

Validate before calling

RepositoryFile f = pur.getFileById(element.getObjectId().getId());
if (f == null || (f.isDeleted() != null && f.isDeleted())) {
  throw new KettleException("File no longer exists (in trash). Use Save As.");
}

Try / catch

try {
  repository.save(element, comment, null, true);
} catch (KettleException e) {
  if (e.getMessage() != null && e.getMessage().contains("Trash")) {
    // prompt user for Save As with a new name/path
  } else throw e;
}

Prevention

When it happens

Trigger: Calling PurRepository.save on a file whose ObjectId resolves to a file in the trash (isInTrash(file) is true) — e.g. saving a transformation that was deleted in the repository browser before the save.

Common situations: Long-running Spoon session where a colleague deleted the transformation mid-edit; saving after accidentally deleting the file; scripts holding stale ObjectIds to deleted files.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at plugins/pur/core/src/main/java/org/pentaho/di/repository/pur/PurRepository.java:3461

    }

    ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.BeforeSaveToRepository.id, element );

    final boolean isUpdate = ( element.getObjectId() != null && element.getObjectId().getId() != null  );
    RepositoryFile file = null;
    if ( isUpdate ) {

      readWriteLock.readLock().lock();
      try {
        ObjectId id = element.getObjectId();
        file = pur.getFileById( id.getId() );

        if ( checkLock && file.isLocked() && !unifiedRepositoryLockService.canUnlockFileById( id ) ) {
          throw new KettleException( "File is currently locked by another user for editing" );
        }
        if ( checkDeleted && isInTrash( file ) ) {
          // absolutely awful to have UI references in this class :(
          throw new KettleException( "File is in the Trash. Use Save As." );
        }
      } finally {
        readWriteLock.readLock().unlock();
      }

      readWriteLock.writeLock().lock();
      try {
        // update title and description
        file =
          new RepositoryFile.Builder( file ).title( RepositoryFile.DEFAULT_LOCALE, element.getName() ).createdDate(
            versionDate != null ? versionDate.getTime() : new Date() ).description( RepositoryFile.DEFAULT_LOCALE,
            Const.NVL( element.getDescription(), "" ) ).build();

        file =
          pur.updateFile( file, new NodeRepositoryFileData( objectTransformer.elementToDataNode( element ) ),
            versionComment );
      } catch ( SOAPFaultException e ) {
        if ( e.getMessage().contains( UnifiedRepositoryUpdateFileException.PREFIX ) ) {

View on GitHub (pinned to f3058517a1)