{"record":{"id":"7404356bae16716b","repo":"pentaho/pentaho-kettle","slug":"unable-to-verify-if-is-null-or-not-because-of-an-error","errorCode":null,"errorMessage":"Unable to verify if [] is null or not because of an error:","messagePattern":"Unable to verify if \\[\\] is null or not because of an error:","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java","lineNumber":3625,"sourceCode":"      }\n\n      if ( emptyStringDiffersFromNull ) {\n        return false;\n      }\n\n      // If it's a string and the string is empty, it's a null value as well\n      //\n      if ( isString() ) {\n        if ( value.toString().length() == 0 ) {\n          return true;\n        }\n      }\n\n      // We tried everything else so we assume this value is not null.\n      //\n      return false;\n    } catch ( ClassCastException e ) {\n      throw new RuntimeException( \"Unable to verify if [\" + toString() + \"] is null or not because of an error:\"\n          + e.toString(), e );\n    }\n  }\n\n  /*\n   * Compare 2 binary strings, one byte at a time.<br> This algorithm is very fast but most likely wrong as well.<br>\n   *\n   * @param one The first binary string to compare with\n   *\n   * @param two the second binary string to compare to\n   *\n   * @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\n   * larger than <i>two</i> protected int compareBinaryStrings(byte[] one, byte[] two) {\n   *\n   * for (int i=0;i<one.length;i++) { if (i>=two.length) return 1; // larger if (one[i]>two[i]) return 1; // larger if\n   * (one[i]<two[i]) return -1; // smaller } if (one.length>two.length) return 1; // larger if (one.length>two.length)\n   * return -11; // smaller return 0; }\n   */","sourceCodeStart":3607,"sourceCodeEnd":3643,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java#L3607-L3643","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.).","Verify the row metadata used for the row actually describes the row (RowMetaInterfacegetRowMeta().searchValueMeta) — fix the upstream step producing mismatched rows.","Before calling isNull, convert data explicitly: e.g. meta.convertData(sourceMeta, data) so the runtime type fits.","Catch RuntimeException around isNull if third-party rows may be malformed, and log the value metadata (toString()) to diagnose."],"exampleFix":"// before\nif (valueMeta.isNull(rowData[i])) { ... } // may throw if type mismatched\n\n// after\nif (rowData[i] == null) { ... }\nelse if (valueMeta.getStorageType() == ValueMetaInterface.STORAGE_TYPE_BINARY_STRING\n    && !(rowData[i] instanceof byte[])) {\n  // treat as data/metadata mismatch; handle explicitly\n} else {\n  if (valueMeta.isNull(rowData[i])) { ... }\n}","handlingStrategy":"type-guard","validationCode":"if (meta.isStorageBinaryString() && !(data instanceof byte[])) {\n  throw new IllegalArgumentException(\"Expected byte[] for binary-string storage on \" + meta.toStringMeta());\n}","typeGuard":"boolean dataMatchesMeta(ValueMetaInterface m, Object data) {\n  if (data == null) return true;\n  if (m.isStorageBinaryString()) return data instanceof byte[];\n  switch (m.getType()) {\n    case ValueMetaInterface.TYPE_STRING: return data instanceof String;\n    case ValueMetaInterface.TYPE_INTEGER: return data instanceof Long;\n    case ValueMetaInterface.TYPE_NUMBER: return data instanceof Double;\n    case ValueMetaInterface.TYPE_BOOLEAN: return data instanceof Boolean;\n    case ValueMetaInterface.TYPE_DATE: return data instanceof java.util.Date;\n    case ValueMetaInterface.TYPE_BIGNUMBER: return data instanceof java.math.BigDecimal;\n    case ValueMetaInterface.TYPE_BINARY: return data instanceof byte[];\n    default: return false;\n  }\n}","tryCatchPattern":"try { boolean isNull = meta.isNull(data); } catch (RuntimeException e) {\n  logger.logError(\"isNull failed, data/metadata mismatch on \" + meta.toStringMeta() + \": \" + e.getMessage());\n}","preventionTips":["Verify row metadata matches actual row objects at step boundaries (RowMetaUtil / trans.getTransMeta().checkRowReferences).","Never reuse one field's ValueMeta to inspect another field's data.","Convert data with meta.convertData(sourceMeta, data) before null/compare operations.","Enable transformation debug row-listener checks during development."],"tags":["pdi","kettle","class-cast","type-mismatch","null-check"],"backgroundTag":"type-mismatch","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}