pentaho/pentaho-kettle · error · KettleStepException
Unable to clone row while copying rows to multiple target…
Error message
Unable to clone row while copying rows to multiple target steps
What it means
When a step distributes rows to multiple target steps in copy mode, BaseStep clones each row (RowMeta.cloneRow) for every extra target. If cloning throws a KettleValueException (e.g. because a field value cannot be copied/cloned by its value metadata), it is wrapped in this KettleStepException. The row's data could not be duplicated for fan-out.
Solutions
- Inspect the chained KettleValueException cause to find which field failed to clone and fix its value data/type.
- Ensure all upstream steps produce standard Kettle ValueData (ValueDataUtil) types matching their declared value metadata.
- Rebuild the row metadata with 'Select values' or 'Get fields' so metadata matches actual data.
- Align plugin/Kettle versions across the classpath to avoid incompatible clone implementations.
Example fix
// before: row built with raw object
Object[] row = { new CustomObject() };
putRow(rowMeta, row);
// after: use a supported type
Object[] row = { customObject.toString() };
putRow(rowMeta, row); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure all fields use standard Kettle value types before fan-out
for (int i = 0; i < rowMeta.size(); i++) {
if (row[i] != null && !rowMeta.getValueMeta(i).getTypeDesc().equals("String") /* etc. */) {
// verify supported type
}
} Type guard
boolean isCloneable(Object[] row) {
return row != null && Arrays.stream(row).allMatch(v -> v == null || v instanceof String || v instanceof Number || v instanceof Date || v instanceof Boolean || v instanceof byte[]);
} Try / catch
try {
putRow(rowMeta, row);
} catch (KettleStepException e) {
if (e.getMessage().contains("Unable to clone row")) {
logError("Non-cloneable row value: " + Arrays.toString(row), e);
} else { throw e; }
} Prevention
- Use only standard Kettle value data types in rows
- Keep plugin versions aligned with the Kettle engine
- Match row data objects to declared RowMeta value types
When it happens
Trigger: putRow() with multiple output rowsets in copy mode while rowMeta.cloneRow(row) fails for the current row — typically due to a value type whose clone implementation cannot handle the stored object (corrupt/foreign value object in the row).
Common situations: Custom value types or plugin steps inserting non-standard Java objects into row fields; Kettle version mismatches where value metadata changed; rows produced by hand-built RowMeta with wrong value data.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unable to clone row while adding rows to the terminator…
- AuthenticationManager.ConsumedTypeError
- AvroInput.Error.UnexpectedRecordFieldTypeAtNonExpansionPoint
- BaseStep.SafeMode.Exception.MixingTypes
- Can't encode object of class : className
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/e6da1df491ec7768.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java:1571
// To reduce stress on the locking system we are NOT going to allow
// the buffer to grow to its full capacity.
//
if ( isUsingThreadPriorityManagment() && !rs.isDone() && rs.size() >= upperBufferBoundary && !isStopped() ) {
try {
Thread.sleep( 0, 1 );
} catch ( InterruptedException e ) {
// Ignore sleep interruption exception
}
}
try {
// Loop until we find room in the target rowset
//
putRowToRowSet( rs, rowMeta, rowMeta.cloneRow( row ) );
incrementLinesWritten();
} catch ( KettleValueException e ) {
throw new KettleStepException( "Unable to clone row while copying rows to multiple target steps", e );
}
}
// set row in first output rowset
//
RowSet rs = outputRowSets.get( 0 );
putRowToRowSet( rs, rowMeta, row );
incrementLinesWritten();
}
}
private void putRowToRowSet( RowSet rs, RowMetaInterface rowMeta, Object[] row ) {
RowMetaInterface toBeSent;
RowMetaInterface metaFromRs = rs.getRowMeta();
if ( metaFromRs == null ) {
// RowSet is not initialised so far
toBeSent = rowMeta.clone();
} else {View on GitHub (pinned to f3058517a1)