diff --git a/lang/java/compiler/pom.xml b/lang/java/compiler/pom.xml
index 53719816387..fe7fbe404bf 100644
--- a/lang/java/compiler/pom.xml
+++ b/lang/java/compiler/pom.xml
@@ -137,6 +137,7 @@
org.apache.avro.compiler.specific.SchemaTask
${project.basedir}/src/test/resources/full_record_v1.avsc
${project.basedir}/src/test/resources/full_record_v2.avsc
+ ${project.basedir}/src/test/resources/regression_error_field_in_record.avsc
${project.basedir}/target/generated-test-sources/javacc
diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
index 1f248ed6daf..e98a50b79a0 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java
@@ -946,19 +946,21 @@ public int getNonNullIndex(Schema s) {
* record.vm can handle the schema being presented.
*/
public boolean isCustomCodable(Schema schema) {
- if (schema.isError())
- return false;
return isCustomCodable(schema, new HashSet<>());
}
private boolean isCustomCodable(Schema schema, Set seen) {
if (!seen.add(schema))
+ // Recursive call: assume custom codable until a caller on the call stack proves
+ // otherwise.
return true;
if (schema.getLogicalType() != null)
return false;
boolean result = true;
switch (schema.getType()) {
case RECORD:
+ if (schema.isError())
+ return false;
for (Schema.Field f : schema.getFields())
result &= isCustomCodable(f.schema(), seen);
break;
diff --git a/lang/java/compiler/src/test/java/org/apache/avro/specific/TestGeneratedCode.java b/lang/java/compiler/src/test/java/org/apache/avro/specific/TestGeneratedCode.java
index 0e61aa7cc15..9a334a45ab0 100644
--- a/lang/java/compiler/src/test/java/org/apache/avro/specific/TestGeneratedCode.java
+++ b/lang/java/compiler/src/test/java/org/apache/avro/specific/TestGeneratedCode.java
@@ -30,6 +30,8 @@
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
+import org.apache.avro.specific.test.RecordWithErrorField;
+import org.apache.avro.specific.test.TestError;
import org.apache.avro.util.Utf8;
import org.junit.Assert;
@@ -92,4 +94,28 @@ void withSchemaMigration() throws IOException {
FullRecordV1 expected = new FullRecordV1(true, 87231, 731L, 54.2832F, 38.0, null, "Hello, world!");
Assert.assertEquals(expected, dst);
}
+
+ @Test
+ public void withErrorField() throws IOException {
+ TestError srcError = TestError.newBuilder().setMessage$("Oops").build();
+ RecordWithErrorField src = new RecordWithErrorField("Hi there", srcError);
+ Assert.assertFalse("Test schema with error field cannot allow for custom coders.",
+ ((SpecificRecordBase) src).hasCustomCoders());
+ Schema schema = RecordWithErrorField.getClassSchema();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
+ Encoder e = EncoderFactory.get().directBinaryEncoder(out, null);
+ DatumWriter w = (DatumWriter) MODEL.createDatumWriter(schema);
+ w.write(src, e);
+ e.flush();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ Decoder d = DecoderFactory.get().directBinaryDecoder(in, null);
+ DatumReader r = (DatumReader) MODEL.createDatumReader(schema);
+ RecordWithErrorField dst = r.read(null, d);
+
+ TestError expectedError = TestError.newBuilder().setMessage$("Oops").build();
+ RecordWithErrorField expected = new RecordWithErrorField("Hi there", expectedError);
+ Assert.assertEquals(expected, dst);
+ }
}
diff --git a/lang/java/compiler/src/test/resources/regression_error_field_in_record.avsc b/lang/java/compiler/src/test/resources/regression_error_field_in_record.avsc
new file mode 100644
index 00000000000..e2fdcb9ad93
--- /dev/null
+++ b/lang/java/compiler/src/test/resources/regression_error_field_in_record.avsc
@@ -0,0 +1,22 @@
+{
+ "type" : "record",
+ "name" : "RecordWithErrorField",
+ "doc" : "With custom coders in Avro 1.9, previously successful records with error fields now fail to compile.",
+ "namespace" : "org.apache.avro.specific.test",
+ "fields" : [ {
+ "name" : "s",
+ "type" : [ "null", "string" ],
+ "default" : null
+ }, {
+ "name": "e",
+ "type": [ "null", {
+ "type" : "error",
+ "name" : "TestError",
+ "fields" : [ {
+ "name" : "message",
+ "type" : "string"
+ } ]
+ } ],
+ "default": null
+ } ]
+}