{"record":{"id":"0163b8d2f4d62085","repo":"apache/flink","slug":"cannot-convert-protobuf-message-with-extension-fie","errorCode":null,"errorMessage":"Cannot convert Protobuf message with extension field(s)","messagePattern":"Cannot convert Protobuf message with extension field\\(s\\)","errorType":"validation","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"flink-formats/flink-parquet/src/main/java/org/apache/flink/formats/parquet/protobuf/PatchedProtoWriteSupport.java","lineNumber":500,"sourceCode":"                        \"Protocol Buffers editions syntax is not supported\");\n            }\n\n            // proto2 uses empty string or \"proto2\", proto3 uses \"proto3\"\n            boolean isProto2 = syntax.isEmpty() || \"proto2\".equals(syntax);\n\n            if (isProto2) {\n                // ============================================================================\n                // END PATCH\n                // ============================================================================\n                // Returns changed fields with values. Map is ordered by id.\n                Map<FieldDescriptor, Object> changedPbFields = pb.getAllFields();\n\n                for (Map.Entry<FieldDescriptor, Object> entry : changedPbFields.entrySet()) {\n                    FieldDescriptor fieldDescriptor = entry.getKey();\n\n                    if (fieldDescriptor.isExtension()) {\n                        // Field index of an extension field might overlap with a base field.\n                        throw new UnsupportedOperationException(\n                                \"Cannot convert Protobuf message with extension field(s)\");\n                    }\n\n                    int fieldIndex = fieldDescriptor.getIndex();\n                    fieldWriters[fieldIndex].writeField(entry.getValue());\n                }\n            } else {\n                // proto3\n                List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();\n                for (FieldDescriptor fieldDescriptor : fieldDescriptors) {\n                    FieldDescriptor.Type type = fieldDescriptor.getType();\n\n                    // For a field in a oneOf that isn't set don't write anything\n                    if (fieldDescriptor.getContainingOneof() != null\n                            && !pb.hasField(fieldDescriptor)) {\n                        continue;\n                    }\n","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-formats/flink-parquet/src/main/java/org/apache/flink/formats/parquet/protobuf/PatchedProtoWriteSupport.java#L482-L518","documentation":"For proto2 messages, the writer dispatches by field index into a prebuilt FieldWriter array; extension fields' indices can collide with base-field indices, so writing a message that actually carries a set extension is rejected with UnsupportedOperationException.","triggerScenarios":"Writing a proto2 Message where getAllFields() includes an extension field (i.e., an extension was populated at runtime via extension registry or builder extension setters).","commonSituations":"Proto2 schemas with extensions (common in older Google/legacy APIs); messages populated by generic tooling that sets extensions; custom options fields materialized as extensions.","solutions":["Do not populate extension fields on messages you write to parquet; clear/strip them before writing","Move the extension fields into the base message definition if they must be persisted","Convert the data to a proto3 message or a parquet row schema that includes those fields explicitly"],"exampleFix":"// before\nbuilder.setExtension(MyExt.extraId, 42);\nout.writeRecord(builder.build()); // throws\n\n// after\n// fold the field into the message definition and use it directly:\n// message User { string name = 1; int64 extra_id = 2; }","handlingStrategy":"validation","validationCode":"boolean hasSetExtensions(MessageOrBuilder pb) {\n  for (FieldDescriptor fd : pb.getAllFields().keySet()) { if (fd.isExtension()) return true; }\n  return false;\n}","typeGuard":null,"tryCatchPattern":"try { writer.write(pb); } catch (UnsupportedOperationException e) { if (e.getMessage().contains(\"extension\")) { /* strip extensions, log, or route to alternative sink */ } throw e; }","preventionTips":["Strip/clear extension fields before writing to parquet","Fold needed extension data into base message fields","Add a unit test asserting your production messages carry no set extensions"],"tags":["parquet","protobuf","extensions","proto2"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}