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
- Open the Avro Output step dialog and set an Avro type for every listed field
- Remove fields that have no meaningful Avro type if they are not needed
- Verify the incoming stream field types are supported; rename/retype unsupported fields (e.g. binary/binary-incompatible types) upstream
- Recreate the step metadata if the transformation was authored in an incompatible Pentaho version
- 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
- Always set the Type column for every field row in the Avro Output dialog
- Validate step metadata programmatically before running transformations
- Check field-type mapping compatibility after upgrading Pentaho or the avro-format plugin
- Delete unused field rows instead of leaving them half-configured
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
- AvroInput.Error.CantLoadIncommingSchemaAndNoDefault
- AvroInput.Error.IncommingSchemaIsMissingAndNoDefault
- AvroInput.Error.UnexpectedArrayElementTypeAtNonExpansionPoin…
- AvroInput.Error.EncounteredAPrimitivePriorToMapExpansion
- AvroInput.Error.MutipleDifferentExpansions
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)