pentaho/pentaho-kettle · error · KettleStepException
BaseStep.Exception.TargetStepToWriteToCantRunInMultipleCopies
BaseStep.Exception.TargetStepToWriteToCantRunInMultipleCopies
Error message
BaseStep.Exception.TargetStepToWriteToCantRunInMultipleCopies
What it means
findOutputRowSet(String targetStep) requires the target step to run in a single copy; it throws KettleStepException when targetStepMeta.getCopies() > 1. With multiple copies there are several target row sets, and writing by name alone cannot pick the right one.
Solutions
- Set the target step's copies to 1, or route through a single dispatcher step before the parallel copies.
- Use the copy-aware overload findOutputRowSet(sourceStep, sourceCopy, targetStep, targetCopy) and pick/iterate copies explicitly.
- Use partitioning on the target so rows are routed per partition key instead of per copy.
- In custom code, write via putRow to all output row sets (getOutputRowSets()) instead of one name-resolved row set.
Example fix
// before
RowSet rs = findOutputRowSet("Parallel Target"); // 4 copies
// after
for (RowSet rs : getOutputRowSets()) { rs.putRow(rowMeta, row); } Defensive patterns
Strategy: validation
Validate before calling
StepMeta tm = transMeta.findStep(targetStepName);
if (tm != null && tm.getCopies() > 1) throw new IllegalArgumentException("Target '" + targetStepName + "' runs " + tm.getCopies() + " copies; write via getOutputRowSets()"); Try / catch
try { RowSet rs = findOutputRowSet(targetStepName); } catch (KettleStepException e) { if (e.getMessage().contains("multiple copies")) { logError("Use copy-aware output writing"); } throw e; } Prevention
- Fan out to parallel targets with putRow, not single row set lookup
- Keep name-addressable target steps at 1 copy
- Prefer partitioning for parallel consumers
- Assert single-copy targets in plugin init()
When it happens
Trigger: Calling findOutputRowSet(targetStep) where the target step is configured with copies N>1 or runs as multiple clustered copies.
Common situations: Target step parallelized via 'Change number of copies' to speed up downstream processing; clustered/slave execution duplicating the target; plugin expecting single-copy fan-out semantics.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- BaseStep.Exception.SourceStepToReadFromCantRunInMultipleCopies
- BaseStep.Exception.SourceStepToReadFromDoesntExist
- BaseStep.Exception.TargetStepToWriteToDoesntExist
- A deadlock was detected between steps
- AddSequence.Exception.NoSpecifiedMethod
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/3411cdf485fd563a.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java:2487
* Find output row set.
*
* @param targetStep the target step
* @return the row set
* @throws KettleStepException the kettle step exception
*/
public RowSet findOutputRowSet( String targetStep ) throws KettleStepException {
// Check to see that "targetStep" only runs in a single copy
// Otherwise you'll see problems during execution.
//
StepMeta targetStepMeta = transMeta.findStep( targetStep );
if ( targetStepMeta == null ) {
throw new KettleStepException( BaseMessages.getString(
PKG, "BaseStep.Exception.TargetStepToWriteToDoesntExist", targetStep ) );
}
if ( targetStepMeta.getCopies() > 1 ) {
throw new KettleStepException( BaseMessages.getString(
PKG, "BaseStep.Exception.TargetStepToWriteToCantRunInMultipleCopies", targetStep, Integer
.toString( targetStepMeta.getCopies() ) ) );
}
return findOutputRowSet( getStepname(), getCopy(), targetStep, 0 );
}
/**
* Find an output rowset in a running transformation. It will also look at the "to" step to see if this is a mapping.
* If it is, it will find the appropriate rowset in that transformation.
*
* @param from
* @param fromcopy
* @param to
* @param tocopy
* @return The rowset or null if none is found.
*/
public RowSet findOutputRowSet( String from, int fromcopy, String to, int tocopy ) {View on GitHub (pinned to f3058517a1)