pentaho/pentaho-kettle · error · KettleException
TransHopMeta.Exception.UnableToSaveTransformationHopInfo
Error message
TransHopMeta.Exception.UnableToSaveTransformationHopInfo
What it means
Thrown by saveTransHopMeta() when inserting a transformation hop (the link between two steps) into the repository fails with a KettleDatabaseException. The transformation ObjectId is appended to the message. Failures in insertTransHop() (FK violations, SQL errors) are wrapped here.
Solutions
- Save all StepMeta objects first so their ObjectIds are set before saving hops
- Verify id_transformation exists (save the transformation header before hops)
- Inspect the chained KettleDatabaseException for the SQL/FK error
- Ensure the DB user has INSERT rights on R_TRANS_HOP
Example fix
// before TransHopMeta hop = new TransHopMeta(stepA, stepB, true); delegate.saveTransHopMeta(hop, transId); // steps not saved yet // after delegate.saveTransformation(transMeta); // persists steps, sets ObjectIds TransHopMeta hop = new TransHopMeta(stepA, stepB, true); delegate.saveTransHopMeta(hop, transId);
Defensive patterns
Strategy: try-catch
Validate before calling
// Java: steps must be persisted before hops
boolean stepsSaved = transHopMeta.getFromStep().getObjectId() != null
&& transHopMeta.getToStep().getObjectId() != null
&& idTransformation != null;
if (!stepsSaved) {
throw new IllegalStateException("Save transformation and steps before hops");
} Try / catch
try {
delegate.saveTransHopMeta(hop, transId);
} catch (KettleException e) {
log.error("Hop save failed for transformation " + transId + ": " + e.getCause(), e);
throw e; // roll back partial save
} Prevention
- Save the transformation header, then steps, then hops — in that order
- Never construct hops from steps lacking ObjectIds when persisting manually
- Check FK errors in the cause chain immediately
- Prefer rep.save(transMeta, ...) which handles ordering internally
When it happens
Trigger: Saving a transformation whose hops reference steps with null ObjectIds (steps not yet saved), or where id_transformation does not exist in R_TRANSFORMATION, causing insertTransHop() to fail.
Common situations: Ordering bug when saving a transformation manually step-by-step before the steps themselves, FK constraint on R_TRANS_HOP pointing at missing R_TRANSFORMATION/R_STEP rows, or DB connection loss during save.
Related errors
- TransDependency.Exception.UnableToSaveTransformationDepency
- TransHopMeta.Exception.LoadTransformationHopInfo
- AccessInputMeta.Exception.ErrorReadingRepository
- AddSequenceMeta.Exception.UnableToReadStepInfo
- AggregateRowsMeta.Exception.UnexpectedErrorWhileReadingStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/10d811e4b3e0c67a.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/repository/kdr/delegates/KettleDatabaseRepositoryTransDelegate.java:1103
} catch ( KettleException dbe ) {
throw new KettleException( BaseMessages.getString(
PKG, "TransDependency.Exception.UnableToSaveTransformationDepency" )
+ id_transformation, dbe );
}
}
public void saveTransHopMeta( TransHopMeta transHopMeta, ObjectId id_transformation ) throws KettleException {
try {
// See if a transformation hop with the same fromstep and tostep is
// already available...
ObjectId id_step_from = transHopMeta.getFromStep() == null ? null : transHopMeta.getFromStep().getObjectId();
ObjectId id_step_to = transHopMeta.getToStep() == null ? null : transHopMeta.getToStep().getObjectId();
// Insert new transMeta hop in repository
transHopMeta.setObjectId( insertTransHop( id_transformation, id_step_from, id_step_to, transHopMeta
.isEnabled() ) );
} catch ( KettleDatabaseException dbe ) {
throw new KettleException( BaseMessages.getString(
PKG, "TransHopMeta.Exception.UnableToSaveTransformationHopInfo" )
+ id_transformation, dbe );
}
}
public TransHopMeta loadTransHopMeta( ObjectId id_trans_hop, List<StepMeta> steps ) throws KettleException {
TransHopMeta hopTransMeta = new TransHopMeta();
try {
hopTransMeta.setObjectId( id_trans_hop );
RowMetaAndData r = getTransHop( id_trans_hop );
hopTransMeta.setEnabled( r.getBoolean( "ENABLED", false ) );
long id_step_from = r.getInteger( "ID_STEP_FROM", 0 );
long id_step_to = r.getInteger( "ID_STEP_TO", 0 );
StepMeta fromStep = StepMeta.findStep( steps, new LongObjectId( id_step_from ) );View on GitHub (pinned to f3058517a1)