{"record":{"id":"57b82e3a4cad8136","repo":"apache/druid","slug":"invalid-struct-type","errorCode":null,"errorMessage":"Invalid Struct type.","messagePattern":"Invalid Struct type\\.","errorType":"exception","errorClass":"InvalidProtocolBufferException","httpStatus":null,"severity":"error","filePath":"extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/ProtobufConverter.java","lineNumber":204,"sourceCode":"    );\n    converters.put(\n        Duration.getDescriptor().getFullName(),\n        msg -> {\n          final Duration duration = Duration.parseFrom(msg.toByteString());\n          return Durations.toString(duration);\n        }\n    );\n    converters.put(\n        FieldMask.getDescriptor().getFullName(),\n        msg -> FieldMaskUtil.toJsonString(FieldMask.parseFrom(msg.toByteString()))\n    );\n    converters.put(\n        Struct.getDescriptor().getFullName(),\n        msg -> {\n          final Descriptors.Descriptor descriptor = msg.getDescriptorForType();\n          final Descriptors.FieldDescriptor field = descriptor.findFieldByName(\"fields\");\n          if (field == null) {\n            throw new InvalidProtocolBufferException(\"Invalid Struct type.\");\n          }\n          // Struct is formatted as a map object.\n          return convertSingleValue(field, msg.getField(field));\n        }\n    );\n    converters.put(\n        Value.getDescriptor().getFullName(),\n        msg -> {\n          final Map<Descriptors.FieldDescriptor, Object> fields = msg.getAllFields();\n          if (fields.isEmpty()) {\n            return null;\n          }\n          if (fields.size() != 1) {\n            throw new InvalidProtocolBufferException(\"Invalid Value type.\");\n          }\n          final Map.Entry<Descriptors.FieldDescriptor, Object> entry = fields.entrySet().stream().findFirst().get();\n          return convertSingleValue(entry.getKey(), entry.getValue());\n        }","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/ProtobufConverter.java#L186-L222","documentation":"Druid registers a specialized converter for google.protobuf.Struct that reads its 'fields' map field. If the descriptor for the message named google.protobuf.Struct has no 'fields' field, the converter throws this InvalidProtocolBufferException because the runtime descriptor deviates from the well-known Struct definition.","triggerScenarios":"convertField encounters a Message whose descriptor full name is google.protobuf.Struct but descriptor.findFieldByName(\"fields\") returns null — i.e. a non-conformant descriptor with that well-known name at conversion time.","commonSituations":"Shaded or outdated protobuf-java on the classpath altering well-known type descriptors; a custom schema registered under the google.protobuf.Struct full name; corrupted schema registry metadata producing a mismatched descriptor.","solutions":["Verify only one protobuf-java version is on the classpath and it matches the one used by the protobuf-extensions module","Re-check the schema stored in the schema registry for the message; re-register the correct .proto if it shadows google.protobuf.Struct","Restart Druid processes after fixing dependencies so descriptors are reloaded"],"exampleFix":"// before: custom .proto message accidentally named google.protobuf.Struct\n// after: use your own package/name for the message\nmessage MyStruct { map<string, Value> fields = 1; }","handlingStrategy":"validation","validationCode":"Descriptors.Descriptor d = msg.getDescriptorForType();\nif (\"google.protobuf.Struct\".equals(d.getFullName()) && d.findFieldByName(\"fields\") == null) {\n  throw new IllegalStateException(\"google.protobuf.Struct descriptor missing 'fields'; descriptor pool is wrong\");\n}","typeGuard":"boolean isValidStruct(Message msg) {\n  return \"google.protobuf.Struct\".equals(msg.getDescriptorForType().getFullName())\n      && msg.getDescriptorForType().findFieldByName(\"fields\") != null;\n}","tryCatchPattern":"try {\n  Map<String, Object> row = ProtobufConverter.convertMessage(msg);\n} catch (InvalidProtocolBufferException e) {\n  if (e.getMessage().contains(\"Invalid Struct type\")) {\n    log.error(\"Struct descriptor mismatch — verify schema registry schema\", e);\n  }\n  throw e;\n}","preventionTips":["Keep exactly one protobuf-java version on the Druid classpath","Validate stored schemas in the registry periodically against well-known types","Never name custom messages with google.protobuf.* full names"],"tags":["protobuf","deserialization","schema"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}