apache/pulsar · error · IllegalArgumentException

Avro Record Builder doesn't support non-avro record as a fie

Error message

Avro Record Builder doesn't support non-avro record as a field

What it means

AvroRecordBuilderImpl.set(String, Object) accepts a nested record value only if it is a GenericAvroRecord (the Avro-backed GenericRecord implementation). If the value is a GenericRecord but of another implementation (e.g. GenericJsonRecord), the builder cannot convert it to an Avro record and throws IllegalArgumentException.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/schema/generic/AvroRecordBuilderImpl.java:53

        this.genericSchema = genericSchema;
        this.avroRecordBuilder =
            new org.apache.avro.generic.GenericRecordBuilder(genericSchema.getAvroSchema());
    }

    /**
     * Sets the value of a field.
     *
     * @param fieldName the name of the field to set.
     * @param value the value to set.
     * @return a reference to the RecordBuilder.
     */
    @Override
    public GenericRecordBuilder set(String fieldName, Object value) {
        if (value instanceof GenericRecord) {
            if (value instanceof GenericAvroRecord) {
                avroRecordBuilder.set(fieldName, ((GenericAvroRecord) value).getAvroRecord());
            } else {
                throw new IllegalArgumentException("Avro Record Builder doesn't support non-avro record as a field");
            }
        } else {
            avroRecordBuilder.set(fieldName, value);
        }
        return this;
    }

    /**
     * Sets the value of a field.
     *
     * @param field the field to set.
     * @param value the value to set.
     * @return a reference to the RecordBuilder.
     */
    @Override
    public GenericRecordBuilder set(Field field, Object value) {
        set(field.getIndex(), value);
        return this;

View on GitHub (pinned to 820761864e)

Solutions

  1. Convert the value to an Avro record first (e.g. use Avro's DatumReader/Writer or decode with the matching Avro generic schema)
  2. Use a GenericAvroRecord for nested fields when building AVRO records
  3. Ensure the nested field's schema type matches the parent record's schema type (AVRO parent -> AVRO child)
  4. If records genuinely differ in type, build the nested Avro record from the JSON node via schema mapping

Example fix

// before
builder.set("nestedField", genericJsonRecord); // IllegalArgumentException
// after
GenericAvroRecord avroNested = (GenericAvroRecord) avroSchemaReader.read(jsonRecordToBytes(genericJsonRecord));
builder.set("nestedField", avroNested);
Defensive patterns

Strategy: type-guard

Validate before calling

if (value instanceof GenericRecord && !(value instanceof GenericAvroRecord)) {
    throw new IllegalArgumentException("Convert nested record to GenericAvroRecord before setting on an Avro builder");
}

Type guard

boolean isAvroGenericRecord(Object v) {
    return !(v instanceof GenericRecord) || v instanceof GenericAvroRecord;
}

Try / catch

try {
    builder.set(fieldName, value);
} catch (IllegalArgumentException e) {
    GenericAvroRecord converted = convertToAvroRecord((GenericRecord) value);
    builder.set(fieldName, converted);
}

Prevention

When it happens

Trigger: Calling RecordBuilder.set(fieldName, value) where value instanceof GenericRecord but not instanceof GenericAvroRecord — typically passing a GenericJsonRecord or a custom GenericRecord implementation into an Avro record builder.

Common situations: Mixing generic schema types across topics: reading a JSON-schema topic with generic schemas and writing the record as a field of an AVRO-schema message; copying fields between records decoded with different generic schema backends.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/d7d03bc2cf29328f. Report an issue: GitHub.