pentaho/pentaho-kettle · error · KettleException

JobHopMeta.Exception.UnableToSaveHopInfoRep

Error message

JobHopMeta.Exception.UnableToSaveHopInfoRep

What it means

KettleDatabaseRepositoryJobDelegate.saveJobHopMeta wraps a KettleDatabaseException thrown while inserting a job hop row into the Kettle database repository tables (R_JOB_HOP). The wrapper includes the internal job id for debugging and chains the underlying database exception as the cause. It means the hop could not be persisted during job save.

Solutions

  1. Check the chained KettleDatabaseException cause for the real database error (constraint, missing table, connection).
  2. Verify the repository schema version matches your Kettle/Pentaho version and run the repository upgrade/repair if needed.
  3. Ensure all job entries save successfully before the hop (fix earlier save errors on the job entries).
  4. Test repository connectivity (db connection, credentials, network) and retry the save after restoring the connection.

Example fix

// before
repository.save(job, versionComment, null, null); // fails mid-way with hop save error
// after
try {
  repository.save(job, versionComment, null, null);
} catch (KettleException e) {
  logError("Job save failed: " + e.getCause(), e); // inspect KettleDatabaseException cause
  // repair repository connection/schema, then retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!repository.isConnected()) throw new IllegalStateException("Repository not connected");
if (hop.getObjectId() == null) { /* ensure from/to job entries already saved with valid ids */ }
// also verify repository schema version:
// repository.getRepositoryMeta().getRepositoryVersion() vs expected Kettle version

Try / catch

try {
  repository.save(job, versionComment, null, null);
} catch (KettleException e) {
  Throwable cause = e.getCause();
  if (cause instanceof KettleDatabaseException) {
    logError("Hop persistence failed: " + cause.getMessage(), cause);
  }
  // restore connection / fix schema, then retry
}

Prevention

When it happens

Trigger: Calling repository.save(job, ...) (or KettleDatabaseRepository.saveJob) when insertJobHop fails: e.g. the referenced job entries (id_jobentry_from/to) were not yet saved (null/invalid object ids), the repository table R_JOB_HOP is missing/corrupt, a database constraint violation occurs (duplicate hop, NULL in required column), or the repository connection was lost.

Common situations: Saving a job in Spoon against a database repository whose schema was not upgraded to the current Pentaho/Kettle version; network/DB outage mid-save; a job hop pointing to a job entry that failed to save; duplicate or self-referencing hop entries violating constraints.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/repository/kdr/delegates/KettleDatabaseRepositoryJobDelegate.java:673

        + id_job_hop ), dbe );

    }
  }

  public void saveJobHopMeta( JobHopMeta hop, ObjectId id_job ) throws KettleException {
    try {
      ObjectId id_jobentry_from = null;
      ObjectId id_jobentry_to = null;

      id_jobentry_from = hop.getFromEntry() == null ? null : hop.getFromEntry().getObjectId();
      id_jobentry_to = hop.getToEntry() == null ? null : hop.getToEntry().getObjectId();

      // Insert new job hop in repository
      //
      hop.setObjectId( insertJobHop( id_job, id_jobentry_from, id_jobentry_to, hop.isEnabled(), hop
        .getEvaluation(), hop.isUnconditional() ) );
    } catch ( KettleDatabaseException dbe ) {
      throw new KettleException( BaseMessages.getString( PKG, "JobHopMeta.Exception.UnableToSaveHopInfoRep", ""
        + id_job ), dbe );

    }
  }

  /**
   * Read the database connections in the repository and add them to this job if they are not yet present.
   *
   * @param jobMeta
   *          the job to put the database connections in
   * @throws KettleException
   */
  public void readDatabases( JobMeta jobMeta ) throws KettleException {
    readDatabases( jobMeta, true );
  }

  /**
   * Read the database connections in the repository and add them to this job if they are not yet present.

View on GitHub (pinned to f3058517a1)