apache/pulsar · error · IllegalArgumentException

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

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.

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.

Appendix: 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)