{"record":{"id":"adb8f420e6b10b33","repo":"pentaho/pentaho-kettle","slug":"joinrowsmeta-exception-unabletosavestepinfotorepository","errorCode":"JoinRowsMeta.Exception.UnableToSaveStepInfoToRepository","errorMessage":"JoinRowsMeta.Exception.UnableToSaveStepInfoToRepository","messagePattern":"JoinRowsMeta\\.Exception\\.UnableToSaveStepInfoToRepository","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/steps/joinrows/JoinRowsMeta.java","lineNumber":274,"sourceCode":"        PKG, \"JoinRowsMeta.Exception.UnexpectedErrorInReadStepInfoFromRepository\" ), e );\n    }\n  }\n\n  @Override\n  public void saveRep( Repository rep, IMetaStore metaStore, ObjectId id_transformation, ObjectId id_step ) throws KettleException {\n    try {\n      rep.saveStepAttribute( id_transformation, id_step, \"directory\", directory );\n      rep.saveStepAttribute( id_transformation, id_step, \"prefix\", prefix );\n      rep.saveStepAttribute( id_transformation, id_step, \"cache_size\", cacheSize );\n\n      if ( mainStepname == null ) {\n        mainStepname = getLookupStepname();\n      }\n      rep.saveStepAttribute( id_transformation, id_step, \"main\", mainStepname );\n\n      rep.saveConditionStepAttribute( id_transformation, id_step, \"id_condition\", condition );\n    } catch ( Exception e ) {\n      throw new KettleException( BaseMessages.getString(\n        PKG, \"JoinRowsMeta.Exception.UnableToSaveStepInfoToRepository\" )\n        + id_step, e );\n    }\n  }\n\n  @Override\n  public void getFields( Bowl bowl, RowMetaInterface rowMeta, String origin, RowMetaInterface[] info, StepMeta nextStep,\n    VariableSpace space, Repository repository, IMetaStore metaStore ) throws KettleStepException {\n    if ( space instanceof TransMeta ) {\n      TransMeta transMeta = (TransMeta) space;\n      StepMeta[] steps = transMeta.getPrevSteps( transMeta.findStep( origin ) );\n      StepMeta mainStep = transMeta.findStep( getMainStepname() );\n      rowMeta.clear();\n      if ( mainStep != null ) {\n        rowMeta.addRowMeta( transMeta.getStepFields( mainStep ) );\n      }\n      for ( StepMeta step : steps ) {\n        if ( mainStep == null || !step.equals( mainStep ) ) {","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/steps/joinrows/JoinRowsMeta.java#L256-L292","documentation":"JoinRowsMeta.saveRep() throws this KettleException when saving the Join Rows step attributes to a repository fails, including saving the main step name and the condition via rep.saveConditionStepAttribute. The message is concatenated with id_step to help locate the offending step. It means persistence of the step definition to the repository failed and the transformation definition is not (fully) saved.","triggerScenarios":"saveRep called during transformation save to a repository; fails when saveStepAttribute or saveConditionStepAttribute throws due to a lost DB connection, transaction/lock timeout, or the referenced lookup step (getLookupStepname()) resolving issues just before attribute writes.","commonSituations":"Repository database down or read-only during save; concurrent saves by two developers causing lock contention; repository quota/disk-full on the DB server; condition referencing a hop/step that was renamed so the saved step name is stale.","solutions":["Check repository DB connectivity and disk space, then re-save the transformation.","Retry the save after confirming no concurrent writer holds locks on the transformation rows.","Verify the join's lookup hop points to an existing, correctly named step so getLookupStepname() resolves before saving.","Export the transformation to .ktr as a workaround to preserve work, then fix repository access and re-save."],"exampleFix":"// before\nrepository.save( transMeta, \"join transform\", null, true ); // saveRep throws on flaky DB\n\n// after ( connectivity check + retry )\nif ( !repository.isConnected() ) repository.connect( \"prod_rep\" );\nint attempts = 0;\nwhile ( true ) {\n  try { repository.save( transMeta, \"join transform\", null, true ); break; }\n  catch ( KettleException e ) {\n    if ( ++attempts >= 3 ) { transMeta.exportToFile( \"backup.ktr\" ); throw e; }\n    Thread.sleep( 1000L * attempts );\n  }\n}","handlingStrategy":"try-catch","validationCode":"if ( !repository.isConnected() || new File( \"/\" ).getUsableSpace() == 0 ) {\n  throw new IllegalStateException( \"Repository unavailable or disk full before save\" );\n}","typeGuard":null,"tryCatchPattern":"try {\n  repository.save( transMeta, versionComment, null, true );\n} catch ( KettleException e ) {\n  if ( e.getMessage() != null && e.getMessage().contains( \"UnableToSaveStepInfoToRepository\" ) ) {\n    transMeta.exportToFile( \"unsaved-backup.ktr\" ); // preserve work before aborting\n    throw new IllegalStateException( \"Repository save failed; work exported to unsaved-backup.ktr\", e );\n  }\n  throw e;\n}","preventionTips":["Avoid two developers saving the same transformation concurrently; use version comments and lock discipline.","Monitor repository DB health and free space before large save operations.","Resolve join lookup hops (no dangling/renamed main or lookup steps) before saving.","Export to .ktr periodically so a failed repository save never loses work."],"tags":["kettle","join-rows","repository","save"],"backgroundTag":"database-write-failed","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}