apache/pulsar · error · IllegalArgumentException

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

Error message

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

What it means

JsonRecordBuilderImpl.set(String, Object) accepts nested GenericRecord values only if they are GenericJsonRecord instances. Any other GenericRecord implementation (e.g. GenericAvroRecord) cannot be converted into a JSON node, so IllegalArgumentException is thrown.

Source

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

    private final GenericSchemaImpl genericSchema;
    private Map<String, Object> map = new HashMap<>();

    public JsonRecordBuilderImpl(GenericSchemaImpl genericSchema) {
        this.genericSchema = genericSchema;
    }

    /**
     * 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 GenericJsonRecord)) {
                throw new IllegalArgumentException("JSON Record Builder doesn't support non-JSON record as a field");
            }
            GenericJsonRecord genericJsonRecord = (GenericJsonRecord) value;
            value = genericJsonRecord.getJsonNode();
        }

        map.put(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) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Convert the Avro record to a JsonNode first (e.g. via Avro's datum writer to JSON encoding) before setting
  2. Use GenericJsonRecord values for nested fields in JSON record builders
  3. Ensure the nested field's schema type matches the parent's (JSON parent -> JSON child)
  4. Decode the source topic with a JSON generic schema if the target builder is JSON

Example fix

// before
jsonBuilder.set("nested", genericAvroRecord); // IllegalArgumentException
// after
JsonNode node = avroToJsonNode(genericAvroRecord);
jsonBuilder.set("nested", node);
Defensive patterns

Strategy: type-guard

Validate before calling

if (value instanceof GenericRecord && !(value instanceof GenericJsonRecord)) {
    throw new IllegalArgumentException("Convert nested record to GenericJsonRecord/JsonNode before setting on a JSON builder");
}

Type guard

boolean canSetOnJsonBuilder(Object v) {
    return !(v instanceof GenericRecord) || v instanceof GenericJsonRecord;
}

Try / catch

try {
    jsonBuilder.set(fieldName, value);
} catch (IllegalArgumentException e) {
    jsonBuilder.set(fieldName, ((GenericAvroRecord) value).getAvroRecord().toString()); // or proper Avro->JsonNode conversion
}

Prevention

When it happens

Trigger: Calling the JSON record builder's set(fieldName, value) with a value that is a GenericRecord but not GenericJsonRecord — typically a GenericAvroRecord from an Avro-schema topic.

Common situations: Copying a field read from an AVRO topic into a JSON-schema message; mixing generic schema backends when aggregating records from multiple topics; refactored code that changed the topic's schema type from AVRO to JSON without changing field construction.

Related errors


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