Skip to content

[Bug][Format][Avro] Mixed-case fields fail during serialization #12008

Description

@goutamadwant

Search before asking

  • I searched the issues and open and closed pull requests and found no report or implementation covering this behavior.

What happened

Avro serialization fails when a SeaTunnelRowType contains a mixed-case field name.

SeaTunnelRowTypeToAvroSchemaConverter preserves the original field name when it creates the Avro schema, but RowToAvroConverter lowercases the field name before passing it to GenericRecordBuilder.set(...).

For example, a row type containing CustomerID produces an Avro schema field named CustomerID, while serialization attempts to set customerid. Avro field lookup is case-sensitive, so the builder cannot find the field and serialization fails before a record is written.

The same mismatch occurs for fields inside a nested ROW.

Expected behavior:

  • The writer uses the same field names as the generated Avro schema.
  • Mixed-case fields serialize at both the top level and inside nested rows.
  • Existing lowercase fields continue to work unchanged.

Actual behavior:

  • Top-level and nested mixed-case fields fail in GenericRecordBuilder.set(...).
  • The failure is in the shared Avro serialization path used by connectors including Kafka and Pulsar.

SeaTunnel Version

Current dev branch at commit 96048a7b81c92d743acc23b5bed652bdfef9d6e5.

SeaTunnel Config

Not configuration-dependent. The issue can be reproduced directly through the Avro conversion API with a SeaTunnelRowType containing mixed-case field names.

Running Command

./mvnw -pl seatunnel-formats/seatunnel-format-avro \
  -Dskip.spotless=true \
  -Dcheckstyle.skip=true \
  -Dlicense.skip=true \
  -Dtest=AvroConverterTest test

Error Exception

With focused top-level and nested regression cases added to AvroConverterTest, both new cases fail:

Tests run: 3, Failures: 0, Errors: 2, Skipped: 0

java.lang.NullPointerException:
Cannot invoke "org.apache.avro.Schema$Field.pos()" because "field" is null

The missing field is the lowercased lookup name, which is not present in the case-preserving schema.

Minimal reproduction

Top-level field:

SeaTunnelRowType rowType =
        new SeaTunnelRowType(
                new String[] {"CustomerID"},
                new SeaTunnelDataType<?>[] {BasicType.INT_TYPE});
SeaTunnelRow row = new SeaTunnelRow(1);
row.setField(0, 42);

new RowToAvroConverter(rowType).convertRowToGenericRecord(row);

Nested field:

SeaTunnelRowType nestedType =
        new SeaTunnelRowType(
                new String[] {"InnerID"},
                new SeaTunnelDataType<?>[] {BasicType.INT_TYPE});
SeaTunnelRowType rowType =
        new SeaTunnelRowType(
                new String[] {"payload"},
                new SeaTunnelDataType<?>[] {nestedType});

SeaTunnelRow nestedRow = new SeaTunnelRow(1);
nestedRow.setField(0, 42);
SeaTunnelRow row = new SeaTunnelRow(1);
row.setField(0, nestedRow);

new RowToAvroConverter(rowType).convertRowToGenericRecord(row);

Root cause

The schema and writer therefore disagree whenever a field name contains an uppercase character.

Impact

The converter is shared by connector serialization paths. Kafka constructs AvroSerializationSchema in its default row serializer, and Pulsar does the same when Avro format is selected:

Any upstream schema that legitimately preserves names such as CustomerID, eventTime, or nested mixed-case fields can reach this failure.

Proposed fix and regression coverage

Use the exact field name in both GenericRecordBuilder.set(...) calls instead of lowercasing it. Add regression coverage for:

  1. A top-level mixed-case field.
  2. A mixed-case field inside a nested ROW.
  3. Conversion back to SeaTunnelRow to confirm a complete round trip.

As a validation of this direction, removing the two lowercase conversions makes both regressions pass. The complete seatunnel-format-avro module then passes with 6 tests, 0 failures, and 0 errors.

This is backward-compatible for existing lowercase field names. It does not change the generated schema, configuration, dependencies, serialized format, or public API; it only makes the writer use the schema's actual field names.

Zeta or Flink or Spark Version

Not engine-specific.

Java or Scala Version

Java 21 was used for reproduction.

Screenshots

Not applicable.

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions