pentaho/pentaho-kettle · error · KettleRowException

BaseStep.SafeMode.Exception.MixingStorageTypes

BaseStep.SafeMode.Exception.MixingStorageTypes

Error message

BaseStep.SafeMode.Exception.MixingStorageTypes

What it means

safeModeChecking compares ValueMetaInterface.getStorageType() positionally; Kettle stores values either as normal Java objects (STORAGE_TYPE_NORMAL) or indexed/binary variants, and if the storage metadata differs between the reference row and the incoming row, KettleRowException is thrown. Same name, type, and arity — but the internal storage representation changed.

Solutions

  1. Normalize the storage metadata in the producing step: clone the ValueMetaInterface and call setStorageType(ValueMetaInterface.STORAGE_TYPE_NORMAL) before putRow.
  2. Insert a 'Select values' step (metadata tab) which re-materializes values into normal storage between the mismatching steps.
  3. Update the plugin step to always build ValueMeta objects with consistent storage metadata in getFields().
  4. As a diagnostic, log rowMeta.toStringMeta() on both sides to see which field's storage metadata differs.

Example fix

// before
ValueMeta vm = (ValueMeta) referenceValue.clone(); // keeps indexed storage
// after
ValueMeta vm = (ValueMeta) referenceValue.clone();
vm.setStorageType(ValueMetaInterface.STORAGE_TYPE_NORMAL);
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 0; i < rowMeta.size(); i++) {
  if (rowMeta.getValueMeta(i).getStorageType() != referenceMeta.getValueMeta(i).getStorageType())
    throw new IllegalStateException("Storage type mismatch at field " + rowMeta.getValueMeta(i).getName());
}

Try / catch

try { putRow(outputRowMeta, row); } catch (KettleRowException e) { if (e.getMessage().contains("MixingStorageTypes")) { logError("Storage metadata differs; normalize with Select values"); } throw e; }

Prevention

When it happens

Trigger: Safe mode enabled; a step emits a row whose ValueMeta has a different storage metadata (e.g. STORAGE_TYPE_INDEXED from a dropdown lookup vs STORAGE_TYPE_NORMAL from a computed value) at position i.

Common situations: Mixing rows coming from a lookup step that returns indexed value metas with rows generated inline; plugin steps that clone value metas but override storage metadata inconsistently; serialization round-trips altering storage metadata.

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/f875fc8b7ea59137. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/step/BaseStep.java:2193

      for ( int i = 0; i < referenceRowMeta.size(); i++ ) {
        ValueMetaInterface referenceValue = referenceRowMeta.getValueMeta( i );
        ValueMetaInterface compareValue = rowMeta.getValueMeta( i );

        if ( !referenceValue.getName().equalsIgnoreCase( compareValue.getName() ) ) {
          throw new KettleRowException( BaseMessages.getString(
            PKG, "BaseStep.SafeMode.Exception.MixingLayout", "" + ( i + 1 ), referenceValue.getName()
              + " " + referenceValue.toStringMeta(), compareValue.getName()
              + " " + compareValue.toStringMeta() ) );
        }

        if ( referenceValue.getType() != compareValue.getType() ) {
          throw new KettleRowException( BaseMessages.getString( PKG, "BaseStep.SafeMode.Exception.MixingTypes", ""
            + ( i + 1 ), referenceValue.getName() + " " + referenceValue.toStringMeta(), compareValue.getName()
            + " " + compareValue.toStringMeta() ) );
        }

        if ( referenceValue.getStorageType() != compareValue.getStorageType() ) {
          throw new KettleRowException( BaseMessages.getString(
            PKG, "BaseStep.SafeMode.Exception.MixingStorageTypes", "" + ( i + 1 ), referenceValue.getName()
              + " " + referenceValue.toStringMeta(), compareValue.getName()
              + " " + compareValue.toStringMeta() ) );
        }
      }
    }
  }

  /**
   * Gets the row from.
   *
   * @param rowSet the row set
   * @return the row from
   * @throws KettleStepException the kettle step exception
   */
  public Object[] getRowFrom( RowSet rowSet ) throws KettleStepException {
    return getRowHandler().getRowFrom( rowSet );
  }

View on GitHub (pinned to f3058517a1)