pentaho/pentaho-kettle · error · KettleException
Unable to find thread with name
Error message
Unable to find thread with name ${stepname} and copy number ${copynr} What it means
Trans.addRowProducer(stepname, copynr) injects rows into an already-prepared transformation step, but first looks up the step's thread. If no running step thread matches the given name and copy number, it throws this KettleException.
Solutions
- Ensure prepareExecution() (or execute()) was called before addRowProducer
- Verify the step name exactly matches the step's name in the transformation (case-sensitive) and the copynr is within its cop ies count
- Print available steps: iterate transMeta.getSteps() to confirm the name/copy you pass
- For transformations loaded from a repository, use the step name as stored, not a renamed label
Example fix
// before
trans.prepareExecution(new String[0]);
RowProducer rp = trans.addRowProducer("Inpu t", 0); // typo
// after
RowProducer rp = trans.addRowProducer("Input", 0); Defensive patterns
Strategy: validation
Validate before calling
// verify the step exists before calling addRowProducer
boolean found = transMeta.getSteps().stream()
.anyMatch(sm -> sm.getName().equals(stepName) && sm.getCopies(transMeta) > copyNr);
if (!found) throw new IllegalArgumentException("No such step: " + stepName + "#" + copyNr);
// and ensure threads exist
trans.prepareExecution(new String[0]); Try / catch
try {
RowProducer rp = trans.addRowProducer(stepName, copyNr);
} catch (KettleException e) {
if (e.getMessage().startsWith("Unable to find thread")) {
throw new IllegalArgumentException("Step/copy not running: " + stepName + "/" + copyNr, e);
} else { throw e; }
} Prevention
- Always call prepareExecution() before addRowProducer
- Step names are case-sensitive — copy the exact name from the transformation
- For steps with multiple copies, remember copynr is 0-based
- Do not rename steps programmatically after prepareExecution but before addRowProducer
When it happens
Trigger: Calling addRowProducer with a step name that doesn't exist in the transformation, a wrong copynr for a multi-copy step, or calling it before prepareExecution()/execute() so threads aren't registered yet.
Common situations: Typo in step name (names are case-sensitive), forgetting that clustered/split transformations run per-slave copies, invoking addRowProducer on a transformation not yet prepared.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- A deadlock was detected between steps
- A module with id ' ' is not defined.
- AbortMeta.Exception.UnexpectedErrorInReadingStepInfoFromRepo…
- AddXML.Exception.FieldNotFound (localized message with…
- AggregateRowsMeta.Exception.UnableToLoadStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/5353f156f608112a.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/Trans.java:3457
public boolean isSafeModeEnabled() {
return safeModeEnabled;
}
/**
* This adds a row producer to the transformation that just got set up. It is preferable to run this BEFORE execute()
* but after prepareExecution()
*
* @param stepname The step to produce rows for
* @param copynr The copynr of the step to produce row for (normally 0 unless you have multiple copies running)
* @return the row producer
* @throws KettleException in case the thread/step to produce rows for could not be found.
* @see Trans#execute(String[])
* @see Trans#prepareExecution(String[])
*/
public RowProducer addRowProducer( String stepname, int copynr ) throws KettleException {
StepInterface stepInterface = getStepInterface( stepname, copynr );
if ( stepInterface == null ) {
throw new KettleException( "Unable to find thread with name " + stepname + " and copy number " + copynr );
}
// We are going to add an extra RowSet to this stepInterface.
RowSet rowSet;
switch ( transMeta.getTransformationType() ) {
case Normal:
rowSet = new BlockingRowSet( transMeta.getSizeRowset() );
break;
case SerialSingleThreaded:
rowSet = new SingleRowRowSet();
break;
case SingleThreaded:
rowSet = new QueueRowSet();
break;
default:
throw new KettleException( "Unhandled transformation type: " + transMeta.getTransformationType() );
}
View on GitHub (pinned to f3058517a1)