pentaho/pentaho-kettle · error · RuntimeException

Unable to verify if [] is null or not because of an error:

Error message

Unable to verify if [] is null or not because of an error:

What it means

ValueMetaBase.isNull(Object) wraps its null-detection logic in a try/catch for ClassCastException and rethrows it as a RuntimeException. The null check casts data (e.g. byte[] for binary-string storage via convertBinaryStringToNativeType), so if the runtime data object does not match the declared value metadata type, the cast fails. This is a data/metadata mismatch, not a null-value question.

Solutions

  1. Align the data with the metadata: ensure the object passed to isNull matches the declared type/storage type (byte[] for binary-string storage, Long for integer, etc.).
  2. Verify the row metadata used for the row actually describes the row (RowMetaInterfacegetRowMeta().searchValueMeta) — fix the upstream step producing mismatched rows.
  3. Before calling isNull, convert data explicitly: e.g. meta.convertData(sourceMeta, data) so the runtime type fits.
  4. Catch RuntimeException around isNull if third-party rows may be malformed, and log the value metadata (toString()) to diagnose.

Example fix

// before
if (valueMeta.isNull(rowData[i])) { ... } // may throw if type mismatched

// after
if (rowData[i] == null) { ... }
else if (valueMeta.getStorageType() == ValueMetaInterface.STORAGE_TYPE_BINARY_STRING
    && !(rowData[i] instanceof byte[])) {
  // treat as data/metadata mismatch; handle explicitly
} else {
  if (valueMeta.isNull(rowData[i])) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (meta.isStorageBinaryString() && !(data instanceof byte[])) {
  throw new IllegalArgumentException("Expected byte[] for binary-string storage on " + meta.toStringMeta());
}

Type guard

boolean dataMatchesMeta(ValueMetaInterface m, Object data) {
  if (data == null) return true;
  if (m.isStorageBinaryString()) return data instanceof byte[];
  switch (m.getType()) {
    case ValueMetaInterface.TYPE_STRING: return data instanceof String;
    case ValueMetaInterface.TYPE_INTEGER: return data instanceof Long;
    case ValueMetaInterface.TYPE_NUMBER: return data instanceof Double;
    case ValueMetaInterface.TYPE_BOOLEAN: return data instanceof Boolean;
    case ValueMetaInterface.TYPE_DATE: return data instanceof java.util.Date;
    case ValueMetaInterface.TYPE_BIGNUMBER: return data instanceof java.math.BigDecimal;
    case ValueMetaInterface.TYPE_BINARY: return data instanceof byte[];
    default: return false;
  }
}

Try / catch

try { boolean isNull = meta.isNull(data); } catch (RuntimeException e) {
  logger.logError("isNull failed, data/metadata mismatch on " + meta.toStringMeta() + ": " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling isNull(Object) (directly or via compare/sort) where the data object's runtime class does not fit the metadata: e.g. metadata says STORAGE_TYPE_BINARY_STRING but data is not byte[], or metadata type is Integer/Date while data is a String/other object passed through convertBinaryStringToNativeType.

Common situations: Rows read from mixed sources where step metadata and actual row layout disagree; reusing a ValueMeta from one field to inspect another field's data; plugin steps that populate rows without honoring the declared row meta; unit tests feeding raw Strings into typed ValueMeta.

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

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java:3625

      }

      if ( emptyStringDiffersFromNull ) {
        return false;
      }

      // If it's a string and the string is empty, it's a null value as well
      //
      if ( isString() ) {
        if ( value.toString().length() == 0 ) {
          return true;
        }
      }

      // We tried everything else so we assume this value is not null.
      //
      return false;
    } catch ( ClassCastException e ) {
      throw new RuntimeException( "Unable to verify if [" + toString() + "] is null or not because of an error:"
          + e.toString(), e );
    }
  }

  /*
   * Compare 2 binary strings, one byte at a time.<br> This algorithm is very fast but most likely wrong as well.<br>
   *
   * @param one The first binary string to compare with
   *
   * @param two the second binary string to compare to
   *
   * @return -1 if <i>one</i> is smaller than <i>two</i>, 0 is both byte arrays are identical and 1 if <i>one</i> is
   * larger than <i>two</i> protected int compareBinaryStrings(byte[] one, byte[] two) {
   *
   * for (int i=0;i<one.length;i++) { if (i>=two.length) return 1; // larger if (one[i]>two[i]) return 1; // larger if
   * (one[i]<two[i]) return -1; // smaller } if (one.length>two.length) return 1; // larger if (one.length>two.length)
   * return -11; // smaller return 0; }
   */

View on GitHub (pinned to f3058517a1)