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

  1. Inspect the chained KettleValueException cause to find which field failed to clone and fix its value data/type.
  2. Ensure all upstream steps produce standard Kettle ValueData (ValueDataUtil) types matching their declared value metadata.
  3. Rebuild the row metadata with 'Select values' or 'Get fields' so metadata matches actual data.
  4. 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

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


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)