pentaho/pentaho-kettle · error · RuntimeException

Field: " + f.getFormatFieldName() + " has undefined type.

Error message

Field: " + f.getFormatFieldName() + " has undefined type. 

What it means

RuntimeException thrown by PentahoAvroOutputFormat.getSchemaObjectNode when building the Avro schema JSON because one of the configured output fields has a null Avro type. Every field must have a concrete AvroSpec.DataType for the schema to be generated.

Solutions

  1. Open the Avro Output step dialog and set an Avro type for every listed field
  2. Remove fields that have no meaningful Avro type if they are not needed
  3. Verify the incoming stream field types are supported; rename/retype unsupported fields (e.g. binary/binary-incompatible types) upstream
  4. Recreate the step metadata if the transformation was authored in an incompatible Pentaho version
  5. Wrap schema generation in a validation loop checking getAvroType() != null before calling schemaObjectNode

Example fix

// before
IAvroOutputField f = fields.next();
if ( f.getAvroType() == null ) { throw new RuntimeException( "Field: " + f.getFormatFieldName() + " has undefined type. " ); }
// after: guard at call site
for ( IAvroOutputField f : outputMeta.getFields() ) {
  if ( f.getAvroType() == null ) { throw new IllegalStateException( "Set an Avro type for field " + f.getFormatFieldName() ); }
}
Defensive patterns

Strategy: validation

Validate before calling

// validate fields before running the transformation
for ( IAvroOutputField f : meta.getAvroFields() ) {
  if ( f.getAvroType() == null ) { throw new IllegalArgumentException( "Set an Avro type for field: " + f.getFormatFieldName() ); }
}

Type guard

boolean hasValidTypes( List<IAvroOutputField> fields ) { return fields.stream().allMatch( f -> f.getAvroType() != null ); }

Try / catch

try { avroOutputFormat.schemaObjectNode(); }
catch ( RuntimeException e ) { if ( e.getMessage().contains( "has undefined type" ) ) { openStepDialogToFixFieldTypes(); } else { throw e; } }

Prevention

When it happens

Trigger: Calling getSchemaObjectNode (via schemaObjectNode) while iterating this.fields and encountering an IAvroOutputField whose getAvroType() returns null — typically a field added in the step dialog without choosing an Avro type, or a field whose type failed to map.

Common situations: User configures the Avro Output step, adds a field row but leaves the Type column unset; a field type from the incoming stream has no Avro equivalent so the mapping produced null; metadata imported from an older/differently-versioned transformation where the type attribute was lost.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at plugins/avro-format/core/src/main/java/org/pentaho/di/trans/steps/avro/output/PentahoAvroOutputFormat.java:179

  }

  protected ObjectNode getSchemaObjectNode() {
    if ( schemaObjectNode == null ) {
      if ( fields != null ) {
        ObjectMapper mapper = new ObjectMapper();
        schemaObjectNode = mapper.createObjectNode();

        schemaObjectNode.put( AvroSpec.NAMESPACE_NODE, nameSpace );
        schemaObjectNode.put( AvroSpec.TYPE_NODE, AvroSpec.TYPE_RECORD );
        schemaObjectNode.put( AvroSpec.NAME_NODE, recordName );
        schemaObjectNode.put( AvroSpec.DOC, docValue );

        ArrayNode fieldNodes = mapper.createArrayNode();
        Iterator<? extends IAvroOutputField> fields = this.fields.iterator();
        while ( fields.hasNext() ) {
          IAvroOutputField f = fields.next();
          if ( f.getAvroType() == null ) {
            throw new RuntimeException( "Field: " + f.getFormatFieldName() + " has undefined type. " );
          }

          AvroSpec.DataType type = f.getAvroType();
          ObjectNode fieldNode = mapper.createObjectNode();

          fieldNode.put( AvroSpec.NAME_NODE, f.getFormatFieldName() );
          if ( type.isPrimitiveType() ) {
            if ( f.getAllowNull() ) {
              ArrayNode arrayNode = mapper.createArrayNode().add( AvroSpec.DataType.NULL.getType() );
              arrayNode.add( type.getType() );
              fieldNode.putPOJO( AvroSpec.TYPE_NODE, arrayNode );
            } else {
              fieldNode.put( AvroSpec.TYPE_NODE, type.getType() );
            }
          } else {
            ObjectNode typeNode = mapper.createObjectNode();
            typeNode.put( AvroSpec.LOGICAL_TYPE, type.getLogicalType() );
            typeNode.put( AvroSpec.TYPE_NODE, type.getBaseType() );

View on GitHub (pinned to f3058517a1)