pentaho/pentaho-kettle · error · KettleException

DatabaseJoinMeta.Exception.UnableToSaveStepInfo + id_step

Error message

DatabaseJoinMeta.Exception.UnableToSaveStepInfo + id_step

What it means

DatabaseJoinMeta.saveRep() wraps any exception thrown while persisting this step's attributes (or inserting the step-database relationship via rep.insertStepDatabase) into a KettleException whose message is 'Unable to save step info' + id_step. It exists so that repository write failures during transformation save carry the offending step id in the message.

Solutions

  1. Read the nested cause to find the underlying repository/SQL error
  2. Verify the repository user has write permission on the target partition
  3. Ensure the associated DatabaseMeta has been saved (non-null ObjectId) before saving the step metadata
  4. Reconnect/retry the repository save once the connection is healthy

Example fix

// before
rep.insertStepDatabase( id_transformation, id_step, databaseMeta.getObjectId() );
// after
if ( databaseMeta != null ) {
  if ( databaseMeta.getObjectId() == null ) {
    rep.save( databaseMeta, id_transformation, metaStore );
  }
  rep.insertStepDatabase( id_transformation, id_step, databaseMeta.getObjectId() );
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before saving a transformation
if ( stepMeta.getDatabaseMeta() != null && stepMeta.getDatabaseMeta().getObjectId() == null ) {
  rep.save( stepMeta.getDatabaseMeta(), transMeta.getObjectId(), metaStore );
}
if ( !rep.isConnected() ) throw new KettleException( "Repository not connected" );

Try / catch

try {
  transMeta.saveRep( rep, metaStore, transId );
} catch ( KettleException e ) {
  if ( e.getMessage() != null && e.getMessage().contains( "Unable to save step info" ) ) {
    logError( "Save failed at step " + e.getMessage() + "; cause=" + e.getCause(), e );
    // reconnect and retry once
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling saveRep() (directly or when saving a transformation to a repository) when any rep.saveStepAttribute* call, rep.insertStepDatabase, or ValueMetaFactory.getNameForValueMeta throws — e.g. read-only repository, lost connection mid-save, or databaseMeta set but its ObjectId is null.

Common situations: Saving a transformation while the repository DB connection was dropped; saving into a read-only user account; a DatabaseMeta that was built in memory and never stored, so getObjectId() is null.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/databasejoin/DatabaseJoinMeta.java:394

    try {
      rep.saveDatabaseMetaStepAttribute( id_transformation, id_step, "id_connection", databaseMeta );
      rep.saveStepAttribute( id_transformation, id_step, "rowlimit", rowLimit );
      rep.saveStepAttribute( id_transformation, id_step, "sql", sql );
      rep.saveStepAttribute( id_transformation, id_step, "outer_join", outerJoin );
      rep.saveStepAttribute( id_transformation, id_step, "replace_vars", replacevars );

      for ( int i = 0; i < parameterField.length; i++ ) {
        rep.saveStepAttribute( id_transformation, id_step, i, "parameter_field", parameterField[i] );
        rep.saveStepAttribute( id_transformation, id_step, i, "parameter_type", ValueMetaFactory
            .getValueMetaName( parameterType[i] ) );
      }

      // Also, save the step-database relationship!
      if ( databaseMeta != null ) {
        rep.insertStepDatabase( id_transformation, id_step, databaseMeta.getObjectId() );
      }
    } catch ( Exception e ) {
      throw new KettleException( BaseMessages.getString( PKG, "DatabaseJoinMeta.Exception.UnableToSaveStepInfo" )
        + id_step, e );
    }
  }

  @Override
  public void check( List<CheckResultInterface> remarks, TransMeta transMeta, StepMeta stepMeta,
    RowMetaInterface prev, String[] input, String[] output, RowMetaInterface info, VariableSpace space,
    Repository repository, IMetaStore metaStore ) {

    CheckResult cr;
    String error_message = "";

    if ( databaseMeta != null ) {
      Database db = new Database( loggingObject, databaseMeta );
      databases = new Database[] { db }; // Keep track of this one for cancelQuery

      try {
        db.connect();

View on GitHub (pinned to f3058517a1)