pentaho/pentaho-kettle · error · KettleException

Unable to find job hop with ID : id_job_hop

Error message

Unable to find job hop with ID : id_job_hop

What it means

Thrown by loadJobHopMeta when a hop is requested by id_job_hop but the repository lookup returns no row, so the hop cannot be found by its ID. The loaded hop would be null otherwise; this explicit error surfaces the missing record instead of returning null.

Solutions

  1. Re-save/reload the job from the repository so hop IDs are current, then retry loading the hop.
  2. Verify the ObjectId actually came from the same repository database.
  3. Remove the dangling hop reference from the job and re-save it.
  4. Check r_jobhop directly in the DB to confirm the row is missing before assuming client bugs.

Example fix

// before: trusting cached hop IDs
jobHop = repo.loadJobHopMeta(new LongObjectId(cachedHopId));

// after: refresh the job to get valid hop IDs
JobMeta fresh = repo.loadJobMeta("MyJob", dir, null);
for (JobHopMeta hop : fresh.getJobhops()) {
  jobHop = repo.loadJobHopMeta(hop.getObjectId());
}
Defensive patterns

Strategy: validation

Validate before calling

// confirm the hop ID exists before loading
List<ObjectId[]> rows = repo.connectionDelegate.getRows(
  "SELECT ID_JOBHOP FROM " + repository.getDatabaseMeta().getQuotedSchemaTableCombination(null, "R_JOBHOP")
  + " WHERE ID_JOBHOP = ?", Collections.singletonList(hopId), 1);
if (rows.isEmpty()) throw new IllegalStateException("Hop " + hopId + " no longer exists in repository");

Type guard

boolean isFreshHopId(JobHopMeta hop, JobMeta currentJob) {
  return hop != null && hop.getObjectId() != null
      && currentJob.getJobhops().stream().anyMatch(h -> hop.getObjectId().equals(h.getObjectId()));
}

Try / catch

try {
  hop = repo.loadJobHopMeta(hopId);
} catch (KettleException e) {
  if (e.getMessage().startsWith("Unable to find job hop")) {
    // stale ID: reload the job and drop the dangling reference
    jobMeta = repo.loadJobMeta(jobname, dir, null);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling repository.loadJobHopMeta(ObjectId id_job_hop) with an ObjectId that has no matching row in r_jobhop — e.g. an ID from a stale/cached job copy, an ID from another repository, or a hop deleted by another user.

Common situations: Concurrent editing where one session deleted hops another session still references; loading a job whose hop rows were removed by a failed transaction; passing an id from a legacy/renumbered repository.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

    try {
      RowMetaAndData r = getJobHop( id_job_hop );
      if ( r != null ) {
        long id_jobentry_copy_from = r.getInteger( "ID_JOBENTRY_COPY_FROM", -1L );
        long id_jobentry_copy_to = r.getInteger( "ID_JOBENTRY_COPY_TO", -1L );

        jobHopMeta.setEnabled( r.getBoolean( "ENABLED", true ) );
        jobHopMeta.setEvaluation( r.getBoolean( "EVALUATION", true ) );
        jobHopMeta.setConditional();
        if ( r.getBoolean( "UNCONDITIONAL", !jobHopMeta.getEvaluation() ) ) {
          jobHopMeta.setUnconditional();
        }

        jobHopMeta.setFromEntry( JobMeta.findJobEntryCopy( jobcopies, new LongObjectId( id_jobentry_copy_from ) ) );
        jobHopMeta.setToEntry( JobMeta.findJobEntryCopy( jobcopies, new LongObjectId( id_jobentry_copy_to ) ) );

        return jobHopMeta;
      } else {
        throw new KettleException( "Unable to find job hop with ID : " + id_job_hop );
      }
    } catch ( KettleDatabaseException dbe ) {
      throw new KettleException( BaseMessages.getString( PKG, "JobHopMeta.Exception.UnableToLoadHopInfoRep", ""
        + 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
      //

View on GitHub (pinned to f3058517a1)