pentaho/pentaho-kettle · error · KettleException

IfNull.Log.CanNotFindField

Error message

IfNull.Log.CanNotFindField

What it means

The If Null step's processRow() resolves each configured field name to an index in the output row metadata. When a configured field name is not present in the incoming row stream, data.fieldnrs[i] is -1, an error is logged, and a KettleException with this message (containing the offending field name) is thrown, aborting the transformation.

Solutions

  1. Open the If Null step dialog, remove/re-select the missing field, and re-save the transformation
  2. Check the upstream step that produces the field and confirm the field actually reaches this step
  3. Enable/inspect the preview of the previous step to see the actual row field names
  4. Make field names case-consistent (Kettle field lookup is case-sensitive)

Example fix

// before (If Null step config referencing removed column)
fields: [ { fieldName: "amnt" } ]
// after (match actual upstream column)
fields: [ { fieldName: "amount" } ]
Defensive patterns

Strategy: validation

Validate before calling

for (String f : ifNullMeta.getFieldNames()) { if (prevRowMeta.indexOfValue(f) < 0) throw new IllegalStateException("Missing field: " + f); }

Type guard

boolean fieldExists(RowMetaInterface row, String name) { return row != null && row.indexOfValue(name) >= 0; }

Try / catch

try { trans.execute(null); } catch (KettleException e) { if (e.getMessage().contains("CanNotFindField")) { fixFieldMapping(); } else { throw e; } }

Prevention

When it happens

Trigger: processRow runs and meta.getFields() contains a field name that indexOfValue() cannot find in the output row metadata — i.e., the configured 'set value if null' field does not exist in the input stream.

Common situations: Upstream step renamed or removed the field; field name typo in the If Null dialog; dynamic field layout from a table input where the column doesn't exist; case-sensitivity mismatch in field names.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/ifnull/IfNull.java:91

      // For String to <type> conversions, we allocate a conversion meta data row as well...
      //
      data.convertRowMeta = data.outputRowMeta.cloneToType( ValueMetaInterface.TYPE_STRING );

      if ( meta.isSelectFields() ) {
        // Consider only selected fields
        if ( meta.getFields() != null && meta.getFields().length > 0 ) {
          int fieldsLength = meta.getFields().length;
          data.fieldnrs = new int[fieldsLength];
          data.defaultValues = new String[fieldsLength];
          data.defaultMasks = new String[fieldsLength];
          data.setEmptyString = new boolean[fieldsLength];

          for ( int i = 0; i < meta.getFields().length; i++ ) {
            data.fieldnrs[i] = data.outputRowMeta.indexOfValue( meta.getFields()[i].getFieldName() );
            if ( data.fieldnrs[i] < 0 ) {
              logError( BaseMessages.getString( PKG, "IfNull.Log.CanNotFindField", meta.getFields()[i]
                  .getFieldName() ) );
              throw new KettleException( BaseMessages.getString( PKG, "IfNull.Log.CanNotFindField", meta.getFields()[i]
                  .getFieldName() ) );
            }
            data.defaultValues[i] = environmentSubstitute( meta.getFields()[i].getReplaceValue() );
            data.defaultMasks[i] = environmentSubstitute( meta.getFields()[i].getReplaceMask() );
            data.setEmptyString[i] = meta.getFields()[i].isSetEmptyString();
          }
        } else {
          throw new KettleException( BaseMessages.getString( PKG, "IfNull.Log.SelectFieldsEmpty" ) );
        }
      } else if ( meta.isSelectValuesType() ) {
        // Consider only select value types
        if ( meta.getValueTypes() != null && meta.getValueTypes().length > 0 ) {
          // return the real default values
          int typeLength = meta.getValueTypes().length;
          data.defaultValues = new String[typeLength];
          data.defaultMasks = new String[typeLength];
          data.setEmptyString = new boolean[typeLength];

View on GitHub (pinned to f3058517a1)