pentaho/pentaho-kettle · warning · KettleDatabaseException

TransMeta.Log.UserCancelledTransSave

TransMeta.Log.UserCancelledTransSave

Error message

User canceled saving of transformation.

What it means

While saving notes during saveTransformation(), the delegate checks the progress monitor before each note and throws a KettleDatabaseException 'User canceled saving of transformation.' when monitor.isCanceled() is true. This is a deliberate cancellation signal, not a database failure, despite the exception type.

Solutions

  1. This is expected user behavior — catch KettleDatabaseException whose message is the cancellation key and treat it as a normal cancel, not an error.
  2. Ensure the transformation remains loadable: a canceled save can leave partial rows; delete or re-save the transformation afterward.
  3. If cancels are unintentional, don't pass an already-canceled monitor, or pass null when no progress reporting is needed.
  4. Inform the user the save was not completed before they close the transformation.

Example fix

// before
repository.save(transMeta, "c", monitor, false);
// after
try {
  repository.save(transMeta, "c", monitor, false);
} catch (KettleDatabaseException e) {
  if (monitor != null && monitor.isCanceled()) {
    log.logBasic("Save cancelled by user");
    return; // treat as cancel, not failure
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (monitor != null && monitor.isCanceled()) return; // don't start a doomed save

Try / catch

try {
  repository.save(transMeta, comment, monitor, false);
} catch (KettleDatabaseException e) {
  if (monitor != null && monitor.isCanceled()) return; // user cancel
  throw e;
}

Prevention

When it happens

Trigger: repository.save(transMeta, comment, monitor, overwrite) with a ProgressMonitor that the user pressed Cancel on while the notes loop (transMeta.nrNotes()) is iterating.

Common situations: User cancels the save progress dialog of a big transformation; UI timeout/cancel button triggers monitor cancellation mid-save; automated callers pass a monitor that is canceled from the start.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/repository/kdr/delegates/KettleDatabaseRepositoryTransDelegate.java:221

        }
        if ( log.isDebug() ) {
          log.logDebug( BaseMessages.getString( PKG, "TransMeta.Log.DeletingOldVersionTransformation" ) );
        }
        repository.deleteTransformation( transMeta.getObjectId() );
        if ( log.isDebug() ) {
          log.logDebug( BaseMessages.getString( PKG, "TransMeta.Log.OldVersionOfTransformationRemoved" ) );
        }
      }
      if ( monitor != null ) {
        monitor.worked( 1 );
      }

      if ( log.isDebug() ) {
        log.logDebug( BaseMessages.getString( PKG, "TransMeta.Log.SavingNotes" ) );
      }
      for ( int i = 0; i < transMeta.nrNotes(); i++ ) {
        if ( monitor != null && monitor.isCanceled() ) {
          throw new KettleDatabaseException( BaseMessages.getString( PKG, "TransMeta.Log.UserCancelledTransSave" ) );
        }

        // if (monitor != null) monitor.subTask(BaseMessages.getString(PKG, "TransMeta.Monitor.SavingNoteTask.Title") +
        // (i + 1) + "/" + transMeta.nrNotes());
        NotePadMeta ni = transMeta.getNote( i );
        repository.saveNotePadMeta( ni, transMeta.getObjectId() );
        if ( ni.getObjectId() != null ) {
          repository.insertTransNote( transMeta.getObjectId(), ni.getObjectId() );
        }
        if ( monitor != null ) {
          monitor.worked( 1 );
        }
      }

      if ( log.isDebug() ) {
        log.logDebug( BaseMessages.getString( PKG, "TransMeta.Log.SavingDatabaseConnections" ) );
      }
      for ( DatabaseMeta databaseMeta : transMeta.getDatabaseManagementInterface().getAll() ) {

View on GitHub (pinned to f3058517a1)