pentaho/pentaho-kettle · error · java.lang.RuntimeException

RuntimeException wrapping clone failure (no message; cause…

Error message

RuntimeException wrapping clone failure (no message; cause is the KettleException from RowMeta copy)

What it means

RowMeta.clone() copies the row metadata via the copy constructor new RowMeta(this, null) under the read lock; if that copy throws any KettleException (e.g. a cloned ValueMeta fails), it is rethrown as an unmodifiable RuntimeException with no message. The meaningful cause is the wrapped KettleException from RowMeta's copy logic.

Solutions

  1. Unwrap and inspect getCause() - the KettleException (and its cause) names the failing value meta
  2. Identify the offending field's ValueMetaInterface and rebuild/normalize it before cloning
  3. Call cloneIntersection or copy via new RowMeta(original) explicitly to localize the failure
  4. Update PDI to a version fixing the specific ValueMeta clone bug if the cause points to a known valueMeta

Example fix

// before
RowMeta copy = (RowMeta) inputRowMeta.clone(); // bare RuntimeException, no message
// after
try {
  RowMeta copy = (RowMeta) inputRowMeta.clone();
} catch ( RuntimeException e ) {
  throw new KettleException( "Failed to clone row metadata", e.getCause() ); // preserve real cause
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ( rowMeta == null || rowMeta.isEmpty() ) {
  throw new IllegalStateException("no row metadata to clone");
}

Type guard

static boolean isCloneSafe(RowMeta rm) {
  return rm != null && rm.size() > 0
    && java.util.Arrays.stream(rm.getValueMetaList()).allMatch(java.util.Objects::nonNull);
}

Try / catch

try {
  RowMeta copy = (RowMeta) rowMeta.clone();
} catch ( RuntimeException e ) {
  // no message on purpose: the KettleException is the cause
  throw new KettleException("RowMeta clone failed", e.getCause());
}

Prevention

When it happens

Trigger: Calling rowMeta.clone() (often via row.clone() in steps) when the underlying value meta list contains a ValueMeta whose clone/copy fails - e.g. corrupt or exotic value metadata, or an internal copy error inside the RowMeta(ValueMetaInterface...) constructor.

Common situations: Cloning row metadata in long-running transformations where a custom/patched value meta implementation misbehaves; concurrency issues if metadata was mutated without the lock; corrupted serialized metadata deserialized from a repository/ktr.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/d0a9289e386379fb. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/row/RowMeta.java:89

        .cloneValueMeta( valueMetaInterface, targetType == null ? valueMetaInterface.getType() : targetType ) );
    }
    this.needRealClone = rowMeta.needRealClone;
  }

  private RowMeta( List<ValueMetaInterface> valueMetaList, RowMetaCache rowMetaCache ) {
    lock = new ReentrantReadWriteLock();
    this.cache = rowMetaCache;
    this.valueMetaList = valueMetaList;
    this.needRealClone = new ArrayList<>();
  }

  @Override
  public RowMeta clone() {
    lock.readLock().lock();
    try {
      return new RowMeta( this, null );
    } catch ( Exception e ) {
      throw new RuntimeException( e );
    } finally {
      lock.readLock().unlock();
    }
  }

  /**
   * This method copies the row metadata and sets all values to the specified type (usually String)
   *
   * @param targetType The target type
   * @return The cloned metadata
   * @throws if the target type could not be loaded from the plugin registry
   */
  @Override
  public RowMetaInterface cloneToType( int targetType ) throws KettleValueException {
    lock.readLock().lock();
    try {
      return new RowMeta( this, targetType );
    } catch ( KettlePluginException e ) {

View on GitHub (pinned to f3058517a1)