{"record":{"id":"8be7f7b40a9a4560","repo":"apache/beam","slug":"infer-schema-of-struct-type-is-not-supported","errorCode":null,"errorMessage":"Infer schema of Struct type is not supported.","messagePattern":"Infer schema of Struct type is not supported\\.","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoSchemaTranslator.java","lineNumber":184,"sourceCode":"  }\n\n  /** Return the proto field number for a type. */\n  static int getFieldNumber(Field field) {\n    return field.getOptions().getValue(SCHEMA_OPTION_META_NUMBER);\n  }\n\n  /** Return a Beam schema representing a proto class. */\n  static Schema getSchema(Class<? extends Message> clazz) {\n    return getSchema(ProtobufUtil.getDescriptorForClass(clazz));\n  }\n\n  static synchronized Schema getSchema(Descriptors.Descriptor descriptor) {\n    if (alreadyVisitedSchemas.containsKey(descriptor)) {\n      @Nullable Schema existingSchema = alreadyVisitedSchemas.get(descriptor);\n      if (existingSchema == null) {\n        String name = descriptor.getFullName();\n        if (\"google.protobuf.Struct\".equals(name)) {\n          throw new UnsupportedOperationException(\"Infer schema of Struct type is not supported.\");\n        }\n        throw new IllegalArgumentException(\n            \"Cannot infer schema with a circular reference. Proto Field: \" + name);\n      }\n      return existingSchema;\n    }\n    alreadyVisitedSchemas.put(descriptor, null);\n    /* OneOfComponentFields refers to the field number in the protobuf where the component subfields\n     * are. This is needed to prevent double inclusion of the component fields.*/\n    Set<Integer> oneOfComponentFields = Sets.newHashSet();\n    /* OneOfFieldLocation stores the field number of the first field in the OneOf. Using this, we can use the location\n    of the first field in the OneOf as the location of the entire OneOf.*/\n    Map<Integer, Field> oneOfFieldLocation = Maps.newHashMap();\n    List<Field> fields = Lists.newArrayListWithCapacity(descriptor.getFields().size());\n\n    // In proto3, an optional field is internally implemented by wrapping it in a synthetic oneof.\n    // The Descriptor.getRealOneOfs() method is then used to retrieve only the \"real\" oneofs that\n    // you explicitly defined, filtering out these automatically generated ones.","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/protobuf/src/main/java/org/apache/beam/sdk/extensions/protobuf/ProtoSchemaTranslator.java#L166-L202","documentation":"When translating a protobuf Descriptor to a Beam Schema, ProtoSchemaTranslator tracks visited descriptors to detect recursion. google.protobuf.Struct (the arbitrary-JSON well-known type) has a self-referential shape Beam cannot represent, so it is explicitly rejected with this UnsupportedOperationException; other cyclic references get a different error.","triggerScenarios":"Translating a proto message that contains a google.protobuf.Struct (or google.protobuf.Value wrapping Struct) field, encountered during recursive schema inference in getSchema.","commonSituations":"Protos embedding JSON payloads via google.protobuf.Struct; using types generated from well-known types in schema-converted PCollections; Beam SQL / schemas over protos containing Struct fields.","solutions":["Replace google.protobuf.Struct fields with a concrete message or a map<string, string> if the data is flat.","Use google.protobuf.Struct outside schema-translated paths (plain ProtoCoder serialization works).","Represent the JSON as bytes/string and parse downstream.","Encode well-known types via a custom type registration if supported by your Beam version."],"exampleFix":"// before\ngoogle.protobuf.Struct metadata = 1;\n// after\nmap<string, string> metadata = 1; // or a concrete message","handlingStrategy":"validation","validationCode":"for (FieldDescriptor f : descriptor.getFields()) {\n  if (f.getType() == FieldDescriptor.Type.MESSAGE\n      && f.getMessageType().getFullName().equals(\"google.protobuf.Struct\")) {\n    throw new IllegalArgumentException(\"Struct field unsupported: \" + f.getName());\n  }\n}","typeGuard":"boolean hasStruct = descriptor.getFields().stream().anyMatch(f ->\n    f.getType() == FieldDescriptor.Type.MESSAGE\n    && f.getMessageType().getFullName().equals(\"google.protobuf.Struct\"));","tryCatchPattern":"try { schema = ProtoSchemaTranslator.getSchema(descriptor); } catch (UnsupportedOperationException e) { /* use bytes/string representation or ProtoCoder */ }","preventionTips":["Avoid google.protobuf.Struct in protos that feed Beam schemas.","Model dynamic JSON as map fields or opaque bytes.","Document supported well-known types for your pipeline."],"tags":["java","protobuf","schema","well-known-types"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}