pentaho/pentaho-kettle · error · KettleException

ERROR0003.UnableToSaveStepToRepository

ERROR0003.UnableToSaveStepToRepository

Error message

DatabaseLookupMeta.ERROR0003.UnableToSaveStepToRepository

What it means

DatabaseLookupMeta.saveRep() wraps any exception raised while persisting the step's attributes (or inserting the step-database relationship) into a KettleException 'Unable to save step information to the repository' + idStep, so a failed transformation save names the offending step.

Solutions

  1. Inspect the nested cause for the underlying repository/SQL error
  2. Ensure the step's DatabaseMeta is saved first (non-null ObjectId) before saving the step
  3. Verify the repository user has write permissions and the DB is writable/reachable
  4. Retry the transformation save after restoring connectivity

Example fix

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

Strategy: try-catch

Validate before calling

// pre-save checks
if ( !rep.isConnected() ) throw new KettleException( "Repository not connected" );
if ( meta.getDatabaseMeta() != null && meta.getDatabaseMeta().getObjectId() == null ) {
  rep.save( meta.getDatabaseMeta(), transMeta.getObjectId(), metaStore );
}

Try / catch

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

Prevention

When it happens

Trigger: Saving a transformation to a repository when rep.saveStepAttribute* fails, rep.insertStepDatabase fails (databaseMeta set but its ObjectId is null or the step row cannot be written), or the repository connection drops.

Common situations: Read-only repository credentials; interrupted saves leaving partial rows; an in-memory DatabaseMeta never stored so getObjectId() is null; repository DB out of space or connection dropped.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/databaselookup/DatabaseLookupMeta.java:629

        rep.saveStepAttribute( idTransformation, idStep, i, TAG_LOOKUP_KEY_FIELD, tableKeyField[ i ] );
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_LOOKUP_KEY_CONDITION, keyCondition[ i ] );
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_LOOKUP_KEY_NAME2, streamKeyField2[ i ] );
      }

      for ( int i = 0; i < returnValueField.length; i++ ) {
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_RETURN_VALUE_NAME, returnValueField[ i ] );
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_RETURN_VALUE_RENAME, returnValueNewName[ i ] );
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_RETURN_VALUE_DEFAULT, returnValueDefault[ i ] );
        rep.saveStepAttribute( idTransformation, idStep, i, TAG_RETURN_VALUE_TYPE, ValueMetaFactory
          .getValueMetaName( returnValueDefaultType[ i ] ) );
      }

      // Also, save the step-database relationship!
      if ( databaseMeta != null ) {
        rep.insertStepDatabase( idTransformation, idStep, databaseMeta.getObjectId() );
      }
    } catch ( Exception e ) {
      throw new KettleException(
        BaseMessages.getString( PKG, "DatabaseLookupMeta.ERROR0003.UnableToSaveStepToRepository" ) + idStep, 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;

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

        db.connect();

        if ( !Utils.isEmpty( tablename ) ) {

View on GitHub (pinned to f3058517a1)