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
- Re-save/reload the job from the repository so hop IDs are current, then retry loading the hop.
- Verify the ObjectId actually came from the same repository database.
- Remove the dangling hop reference from the job and re-save it.
- 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
- Reload the owning job before using hop ObjectIds cached from earlier sessions.
- Never carry ObjectIds across repositories or environments.
- Avoid concurrent edits; use repository versioning/locking where available.
- Clean up dangling hops when deleting job entries (delete hops referencing the entry first).
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
- I couldn't find Notepad with id_note=id_note in the…
- AutoDoc.Exception.RepositoryDirectoryNotFound
- Could not find folder [ + id + ]
- File is in the Trash. Use Save As.
- JobHopMeta.Exception.UnableToLoadHopInfoRep
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)