Repository: grpc-ecosystem/grpc-gateway

Issue:
protoc-gen-openapiv2 violation of strict rule `no-$ref-siblings`
## 🐛 Bug Report

When using protoc-gen-openapiv2, the generated open api v2 definition contains undesired:

```
{
  ...
  "items": {
    "type": "object",
    "$ref": "#/definitions/demoItemWithChoice"
  },
  ...
}
```

## To Reproduce

1. clone https://github.com/jgreffe/protoc-gen-openapiv2-siblings
2. follow readme.md

## Expected behavior
Generated definition should contain `$ref` without siblings:
```
{
  ...
  "items": {
    "$ref": "#/definitions/demoItemWithChoice"
  },
  ...
}
```


## Actual Behavior
```
{
  ...
  "items": {
    "type": "object",
    "$ref": "#/definitions/demoItemWithChoice"
  },
  ...
}
```


## Your Environment
N/A


Discussion:
@johanbrandhorst: This sounds similar to the discussion in https://github.com/grpc-ecosystem/grpc-gateway/issues/3058, could you have a look through that?@jgreffe: Updated my [example](https://github.com/jgreffe/protoc-gen-openapiv2-siblings) with the last `github.com/grpc-ecosystem/grpc-gateway/v2` version `v2.27.3`: still getting the same issue, so https://github.com/grpc-ecosystem/grpc-gateway/pull/5827 doesn't fix it.

Solution patch:
diff --git a/MODULE.bazel b/MODULE.bazel
index 0387f09d..e58737dc 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -14,7 +14,7 @@ bazel_dep(name = "rules_go", version = "0.59.0", repo_name = "io_bazel_rules_go"
 bazel_dep(name = "rules_shell", version = "0.6.1")
 bazel_dep(name = "gazelle", version = "0.47.0", repo_name = "bazel_gazelle")
 bazel_dep(name = "bazel_skylib", version = "1.8.2")
-bazel_dep(name = "protobuf", version = "33.0", repo_name = "com_google_protobuf")
+bazel_dep(name = "protobuf", version = "33.1", repo_name = "com_google_protobuf")
 bazel_dep(name = "googleapis", version = "0.0.0-20251111-659ea6e9")
 
 # This is required as a transitive dependency and not directly needed by this module.
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 3bfed3ad..375bba2c 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -98,8 +98,8 @@
     "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
     "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573",
     "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
-    "https://bcr.bazel.build/modules/protobuf/33.0/MODULE.bazel": "c5270efb4aad37a2f893536076518793f409ea7df07a06df995d848d1690f21c",
-    "https://bcr.bazel.build/modules/protobuf/33.0/source.json": "cd7ac80ad863190b9151281a85acc11d77b5bde2ba56443a7215da2d4ace6da3",
+    "https://bcr.bazel.build/modules/protobuf/33.1/MODULE.bazel": "982c8a0cceab4d790076f72b7677faf836b0dfadc2b66a34aab7232116c4ae39",
+    "https://bcr.bazel.build/modules/protobuf/33.1/source.json": "992c237a40899425648213bf79b05f08c6e8dcd619f96cd944b4511b0276fbd8",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
diff --git a/WORKSPACE b/WORKSPACE
index 9096d627..b986eb2f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -35,17 +35,17 @@ http_archive(
 # Define before rules_proto, otherwise we receive the version of com_google_protobuf from there
 http_archive(
     name = "com_google_protobuf",
-    sha256 = "b6b03fbaa3a90f3d4f2a3fa4ecc41d7cd0326f92fcc920a7843f12206c8d52cd",
-    strip_prefix = "protobuf-33.0",
-    urls = ["https://github.com/protocolbuffers/protobuf/archive/v33.0.tar.gz"],
+    sha256 = "0c98bb704ceb4e68c92f93907951ca3c36130bc73f87264e8c0771a80362ac97",
+    strip_prefix = "protobuf-33.1",
+    urls = ["https://github.com/protocolbuffers/protobuf/archive/v33.1.tar.gz"],
 )
 
 http_archive(
     name = "googleapis",
-    sha256 = "53b620698a1b21a206a7d809153294988c0bbf4783b4179c26869a3d7aecaee8",
-    strip_prefix = "googleapis-cf0434f4bd20618db60ddd16a1e7db2c0dfb9158",
+    sha256 = "6782b6a22456bd83a2991634b4d8f9796a2644c2fcc24d68784859a1e0040c41",
+    strip_prefix = "googleapis-04085e583e40730b26a15194cd73f50626c08110",
     urls = [
-        "https://github.com/googleapis/googleapis/archive/cf0434f4bd20618db60ddd16a1e7db2c0dfb9158.zip",
+        "https://github.com/googleapis/googleapis/archive/04085e583e40730b26a15194cd73f50626c08110.zip",
     ],
 )
 
diff --git a/docs/docs/mapping/customizing_openapi_output.md b/docs/docs/mapping/customizing_openapi_output.md
index 3a079ef0..7a81ef76 100644
--- a/docs/docs/mapping/customizing_openapi_output.md
+++ b/docs/docs/mapping/customizing_openapi_output.md
@@ -861,6 +861,99 @@ definitions:
         type: string
 ```
 
+### Omit array item type when $ref is a sibling
+
+By default, when generating OpenAPI schemas for array items that reference other definitions, both `type: object` and `$ref` properties are included. However, some strict OpenAPI validators enforce the `no-$ref-siblings` rule, which prohibits having `$ref` alongside other properties at the same level. To comply with this strict validation, you can use the `omit_array_item_type_when_ref_sibling` option. When enabled, this option omits the `type: object` property from array items when a `$ref` is present, since `$ref` already implies the type. Allowed values are: `true`, `false`.
+
+**Note**: This option only affects the generated OpenAPI specification and does not alter the behavior of the gateway itself.
+
+For example, if you are using `buf`:
+
+```yaml
+version: v1
+plugins:
+  - name: openapiv2
+    out: .
+    opt:
+      - omit_array_item_type_when_ref_sibling=true
+```
+
+or with `protoc`
+
+```sh
+protoc --openapiv2_out=. --openapiv2_opt=omit_array_item_type_when_ref_sibling=true ./path/to/file.proto
+```
+
+Input example:
+
+```protobuf
+syntax = "proto3";
+
+package example.v1;
+
+import "google/api/annotations.proto";
+
+option go_package = "example/v1;example";
+
+service ExampleService {
+  rpc GetItems(GetItemsRequest) returns (GetItemsResponse) {
+    option (google.api.http) = {get: "/api/items"};
+  }
+}
+
+message GetItemsRequest {}
+
+message GetItemsResponse {
+  repeated Item items = 1;
+}
+
+message Item {
+  string id = 1;
+  string name = 2;
+}
+```
+
+Output without the option (default behavior):
+
+```yaml
+definitions:
+  v1GetItemsResponse:
+    type: object
+    properties:
+      items:
+        type: array
+        items:
+          type: object  # This can violate strict no-$ref-siblings rule
+          $ref: "#/definitions/v1Item"
+  v1Item:
+    type: object
+    properties:
+      id:
+        type: string
+      name:
+        type: string
+```
+
+Output with `omit_array_item_type_when_ref_sibling=true`:
+
+```yaml
+definitions:
+  v1GetItemsResponse:
+    type: object
+    properties:
+      items:
+        type: array
+        items:
+          $ref: "#/definitions/v1Item"  # type: object is omitted
+  v1Item:
+    type: object
+    properties:
+      id:
+        type: string
+      name:
+        type: string
+```
+
 ### Custom HTTP Header Request Parameters
 
 By default the parameters for each operation are generated from the protocol buffer definition however you can extend the parameters to include extra HTTP headers if required.
diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go
index d2b71500..b057980a 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -157,6 +157,10 @@ type Registry struct {
 	// properties
 	useAllOfForRefs bool
 
+	// omitArrayItemTypeWhenRefSibling, if set, will omit 'type: object' in array items when $ref is present
+	// to avoid no-$ref-siblings violations in OpenAPI v2
+	omitArrayItemTypeWhenRefSibling bool
+
 	// allowPatchFeature determines whether to use PATCH feature involving update masks (using google.protobuf.FieldMask).
 	allowPatchFeature bool
 
@@ -888,6 +892,16 @@ func (r *Registry) GetUseAllOfForRefs() bool {
 	return r.useAllOfForRefs
 }
 
+// SetOmitArrayItemTypeWhenRefSibling sets omitArrayItemTypeWhenRefSibling
+func (r *Registry) SetOmitArrayItemTypeWhenRefSibling(omit bool) {
+	r.omitArrayItemTypeWhenRefSibling = omit
+}
+
+// GetOmitArrayItemTypeWhenRefSibling returns omitArrayItemTypeWhenRefSibling
+func (r *Registry) GetOmitArrayItemTypeWhenRefSibling() bool {
+	return r.omitArrayItemTypeWhenRefSibling
+}
+
 // SetAllowPatchFeature sets allowPatchFeature
 func (r *Registry) SetAllowPatchFeature(allow bool) {
 	r.allowPatchFeature = allow
diff --git a/protoc-gen-openapiv2/defs.bzl b/protoc-gen-openapiv2/defs.bzl
index b25736c4..85d3dad8 100644
--- a/protoc-gen-openapiv2/defs.bzl
+++ b/protoc-gen-openapiv2/defs.bzl
@@ -74,6 +74,7 @@ def _run_proto_gen_openapi(
         generate_unbound_methods,
         visibility_restriction_selectors,
         use_allof_for_refs,
+        omit_array_item_type_when_ref_sibling,
         disable_default_responses,
         enable_rpc_deprecation,
         enable_field_deprecation,
@@ -150,6 +151,9 @@ def _run_proto_gen_openapi(
     if use_allof_for_refs:
         args.add("--openapiv2_opt", "use_allof_for_refs=true")
 
+    if omit_array_item_type_when_ref_sibling:
+        args.add("--openapiv2_opt", "omit_array_item_type_when_ref_sibling=true")
+
     if disable_default_responses:
         args.add("--openapiv2_opt", "disable_default_responses=true")
 
@@ -273,6 +277,7 @@ def _proto_gen_openapi_impl(ctx):
                     generate_unbound_methods = ctx.attr.generate_unbound_methods,
                     visibility_restriction_selectors = ctx.attr.visibility_restriction_selectors,
                     use_allof_for_refs = ctx.attr.use_allof_for_refs,
+                    omit_array_item_type_when_ref_sibling = ctx.attr.omit_array_item_type_when_ref_sibling,
                     disable_default_responses = ctx.attr.disable_default_responses,
                     enable_rpc_deprecation = ctx.attr.enable_rpc_deprecation,
                     enable_field_deprecation = ctx.attr.enable_field_deprecation,
@@ -427,6 +432,12 @@ protoc_gen_openapiv2 = rule(
             doc = "if set, will use allOf as container for $ref to preserve" +
                   " same-level properties.",
         ),
+        "omit_array_item_type_when_ref_sibling": attr.bool(
+            default = False,
+            mandatory = False,
+            doc = "if set, will omit 'type: object' in array items when $ref is present" +
+                  " to avoid strict no-$ref-siblings rule violations.",
+        ),
         "disable_default_responses": attr.bool(
             default = False,
             mandatory = False,
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template.go b/protoc-gen-openapiv2/internal/genopenapi/template.go
index e4033754..3c7698f0 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template.go
@@ -865,8 +865,13 @@ func schemaOfFieldBase(f *descriptor.Field, reg *descriptor.Registry, refs refMa
 
 	switch aggregate {
 	case array:
-		if _, ok := wktSchemas[fd.GetTypeName()]; !ok && fd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
-			core.Type = "object"
+		// Only set core.Type = "object" for MESSAGE types with $ref if the flag is not set.
+		// When omitArrayItemTypeWhenRefSibling is true, we omit "type: object" to avoid
+		// no-$ref-siblings violations in OpenAPI v2, since $ref already implies the type is object.
+		if !reg.GetOmitArrayItemTypeWhenRefSibling() {
+			if _, ok := wktSchemas[fd.GetTypeName()]; !ok && fd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
+				core.Type = "object"
+			}
 		}
 		ret = openapiSchemaObject{
 			schemaCore: schemaCore{
diff --git a/protoc-gen-openapiv2/main.go b/protoc-gen-openapiv2/main.go
index f8fa9a31..2450973b 100644
--- a/protoc-gen-openapiv2/main.go
+++ b/protoc-gen-openapiv2/main.go
@@ -17,43 +17,44 @@ import (
 )
 
 var (
-	importPrefix                   = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
-	file                           = flag.String("file", "-", "where to load data from")
-	allowDeleteBody                = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
-	grpcAPIConfiguration           = flag.String("grpc_api_configuration", "", "path to file which describes the gRPC API Configuration in YAML format")
-	allowMerge                     = flag.Bool("allow_merge", false, "if set, generation one OpenAPI file out of multiple protos")
-	mergeFileName                  = flag.String("merge_file_name", "apidocs", "target OpenAPI file name prefix after merge")
-	useJSONNamesForFields          = flag.Bool("json_names_for_fields", true, "if disabled, the original proto name will be used for generating OpenAPI definitions")
-	repeatedPathParamSeparator     = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`")
-	versionFlag                    = flag.Bool("version", false, "print the current version")
-	_                              = flag.Bool("allow_repeated_fields_in_body", true, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option. DEPRECATED: the value is ignored and always behaves as `true`.")
-	includePackageInTags           = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. If set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
-	useFQNForOpenAPIName           = flag.Bool("fqn_for_openapi_name", false, "if set, the object's OpenAPI names will use the fully qualified names from the proto definition (ie my.package.MyMessage.MyInnerMessage). DEPRECATED: prefer `openapi_naming_strategy=fqn`")
-	openAPINamingStrategy          = flag.String("openapi_naming_strategy", "", "use the given OpenAPI naming strategy. Allowed values are `legacy`, `fqn`, `simple`, `package`. If unset, either `legacy` or `fqn` are selected, depending on the value of the `fqn_for_openapi_name` flag")
-	useGoTemplate                  = flag.Bool("use_go_templates", false, "if set, you can use Go templates in protofile comments")
-	goTemplateArgs                 = utilities.StringArrayFlag(flag.CommandLine, "go_template_args", "provide a custom value that can override a key in the Go template. Requires the `use_go_templates` option to be set")
-	ignoreComments                 = flag.Bool("ignore_comments", false, "if set, all protofile comments are excluded from output")
-	removeInternalComments         = flag.Bool("remove_internal_comments", false, "if set, removes all substrings in comments that start with `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments")
-	disableDefaultErrors           = flag.Bool("disable_default_errors", false, "if set, disables generation of default errors. This is useful if you have defined custom error handling")
-	enumsAsInts                    = flag.Bool("enums_as_ints", false, "whether to render enum values as integers, as opposed to string values")
-	simpleOperationIDs             = flag.Bool("simple_operation_ids", false, "whether to remove the service prefix in the operationID generation. Can introduce duplicate operationIDs, use with caution.")
-	proto3OptionalNullable         = flag.Bool("proto3_optional_nullable", false, "whether Proto3 Optional fields should be marked as x-nullable")
-	openAPIConfiguration           = flag.String("openapi_configuration", "", "path to file which describes the OpenAPI Configuration in YAML format")
-	generateUnboundMethods         = flag.Bool("generate_unbound_methods", false, "generate swagger metadata even for RPC methods that have no HttpRule annotation")
-	recursiveDepth                 = flag.Int("recursive-depth", 1000, "maximum recursion count allowed for a field type")
-	omitEnumDefaultValue           = flag.Bool("omit_enum_default_value", false, "if set, omit default enum value")
-	outputFormat                   = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
-	visibilityRestrictionSelectors = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
-	disableServiceTags             = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
-	disableDefaultResponses        = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
-	useAllOfForRefs                = flag.Bool("use_allof_for_refs", false, "if set, will use allOf as container for $ref to preserve same-level properties.")
-	allowPatchFeature              = flag.Bool("allow_patch_feature", true, "whether to hide update_mask fields in PATCH requests from the generated swagger file.")
-	preserveRPCOrder               = flag.Bool("preserve_rpc_order", false, "if true, will ensure the order of paths emitted in openapi swagger files mirror the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically.")
-	enableRpcDeprecation           = flag.Bool("enable_rpc_deprecation", false, "whether to process grpc method's deprecated option.")
-	enableFieldDeprecation         = flag.Bool("enable_field_deprecation", false, "whether to process proto field's deprecated option.")
-	expandSlashedPathPatterns      = flag.Bool("expand_slashed_path_patterns", false, "if set, expands path parameters with URI sub-paths into the URI. For example, \"/v1/{name=projects/*}/resource\" becomes \"/v1/projects/{project}/resource\".")
-	useProto3FieldSemantics        = flag.Bool("use_proto3_field_semantics", false, "if set, uses proto3 field semantics for the OpenAPI schema. This means that fields are required by default.")
-	generateXGoType                = flag.Bool("generate_x_go_type", false, "if set, generates x-go-type extension using the go_package option from proto files")
+	importPrefix                    = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
+	file                            = flag.String("file", "-", "where to load data from")
+	allowDeleteBody                 = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
+	grpcAPIConfiguration            = flag.String("grpc_api_configuration", "", "path to file which describes the gRPC API Configuration in YAML format")
+	allowMerge                      = flag.Bool("allow_merge", false, "if set, generation one OpenAPI file out of multiple protos")
+	mergeFileName                   = flag.String("merge_file_name", "apidocs", "target OpenAPI file name prefix after merge")
+	useJSONNamesForFields           = flag.Bool("json_names_for_fields", true, "if disabled, the original proto name will be used for generating OpenAPI definitions")
+	repeatedPathParamSeparator      = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`")
+	versionFlag                     = flag.Bool("version", false, "print the current version")
+	_                               = flag.Bool("allow_repeated_fields_in_body", true, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option. DEPRECATED: the value is ignored and always behaves as `true`.")
+	includePackageInTags            = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. If set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
+	useFQNForOpenAPIName            = flag.Bool("fqn_for_openapi_name", false, "if set, the object's OpenAPI names will use the fully qualified names from the proto definition (ie my.package.MyMessage.MyInnerMessage). DEPRECATED: prefer `openapi_naming_strategy=fqn`")
+	openAPINamingStrategy           = flag.String("openapi_naming_strategy", "", "use the given OpenAPI naming strategy. Allowed values are `legacy`, `fqn`, `simple`, `package`. If unset, either `legacy` or `fqn` are selected, depending on the value of the `fqn_for_openapi_name` flag")
+	useGoTemplate                   = flag.Bool("use_go_templates", false, "if set, you can use Go templates in protofile comments")
+	goTemplateArgs                  = utilities.StringArrayFlag(flag.CommandLine, "go_template_args", "provide a custom value that can override a key in the Go template. Requires the `use_go_templates` option to be set")
+	ignoreComments                  = flag.Bool("ignore_comments", false, "if set, all protofile comments are excluded from output")
+	removeInternalComments          = flag.Bool("remove_internal_comments", false, "if set, removes all substrings in comments that start with `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments")
+	disableDefaultErrors            = flag.Bool("disable_default_errors", false, "if set, disables generation of default errors. This is useful if you have defined custom error handling")
+	enumsAsInts                     = flag.Bool("enums_as_ints", false, "whether to render enum values as integers, as opposed to string values")
+	simpleOperationIDs              = flag.Bool("simple_operation_ids", false, "whether to remove the service prefix in the operationID generation. Can introduce duplicate operationIDs, use with caution.")
+	proto3OptionalNullable          = flag.Bool("proto3_optional_nullable", false, "whether Proto3 Optional fields should be marked as x-nullable")
+	openAPIConfiguration            = flag.String("openapi_configuration", "", "path to file which describes the OpenAPI Configuration in YAML format")
+	generateUnboundMethods          = flag.Bool("generate_unbound_methods", false, "generate swagger metadata even for RPC methods that have no HttpRule annotation")
+	recursiveDepth                  = flag.Int("recursive-depth", 1000, "maximum recursion count allowed for a field type")
+	omitEnumDefaultValue            = flag.Bool("omit_enum_default_value", false, "if set, omit default enum value")
+	outputFormat                    = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
+	visibilityRestrictionSelectors  = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
+	disableServiceTags              = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
+	disableDefaultResponses         = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
+	useAllOfForRefs                 = flag.Bool("use_allof_for_refs", false, "if set, will use allOf as container for $ref to preserve same-level properties.")
+	omitArrayItemTypeWhenRefSibling = flag.Bool("omit_array_item_type_when_ref_sibling", false, "if set, will omit 'type: object' in array items when $ref is present to avoid strict no-$ref-siblings rule violations.")
+	allowPatchFeature               = flag.Bool("allow_patch_feature", true, "whether to hide update_mask fields in PATCH requests from the generated swagger file.")
+	preserveRPCOrder                = flag.Bool("preserve_rpc_order", false, "if true, will ensure the order of paths emitted in openapi swagger files mirror the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically.")
+	enableRpcDeprecation            = flag.Bool("enable_rpc_deprecation", false, "whether to process grpc method's deprecated option.")
+	enableFieldDeprecation          = flag.Bool("enable_field_deprecation", false, "whether to process proto field's deprecated option.")
+	expandSlashedPathPatterns       = flag.Bool("expand_slashed_path_patterns", false, "if set, expands path parameters with URI sub-paths into the URI. For example, \"/v1/{name=projects/*}/resource\" becomes \"/v1/projects/{project}/resource\".")
+	useProto3FieldSemantics         = flag.Bool("use_proto3_field_semantics", false, "if set, uses proto3 field semantics for the OpenAPI schema. This means that fields are required by default.")
+	generateXGoType                 = flag.Bool("generate_x_go_type", false, "if set, generates x-go-type extension using the go_package option from proto files")
 
 	_ = flag.Bool("logtostderr", false, "Legacy glog compatibility. This flag is a no-op, you can safely remove it")
 )
@@ -175,6 +176,7 @@ func main() {
 	reg.SetDisableServiceTags(*disableServiceTags)
 	reg.SetDisableDefaultResponses(*disableDefaultResponses)
 	reg.SetUseAllOfForRefs(*useAllOfForRefs)
+	reg.SetOmitArrayItemTypeWhenRefSibling(*omitArrayItemTypeWhenRefSibling)
 	reg.SetAllowPatchFeature(*allowPatchFeature)
 	reg.SetPreserveRPCOrder(*preserveRPCOrder)
 	reg.SetEnableRpcDeprecation(*enableRpcDeprecation)



Test patch:
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template_test.go b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
index ebcd2070..a7ae2213 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template_test.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
@@ -10498,6 +10498,247 @@ func TestArrayMessageItemsType(t *testing.T) {
 	}
 }
 
+func TestArrayMessageItemsTypeOmitWhenRefSibling(t *testing.T) {
+	msgDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("ExampleMessage"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("children"),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".example.ExampleMessage"),
+				Number:   proto.Int32(1),
+				JsonName: proto.String("children"),
+			},
+		},
+	}
+
+	nestDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("NestDescMessage"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("children"),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".example.ExampleMessage"),
+				Number:   proto.Int32(1),
+				JsonName: proto.String("children"),
+			},
+		},
+	}
+
+	meth := &descriptorpb.MethodDescriptorProto{
+		Name:       proto.String("Example"),
+		InputType:  proto.String("ExampleMessage"),
+		OutputType: proto.String("NestDescMessage"),
+	}
+	svc := &descriptorpb.ServiceDescriptorProto{
+		Name:   proto.String("ExampleService"),
+		Method: []*descriptorpb.MethodDescriptorProto{meth},
+	}
+	msg := &descriptor.Message{
+		DescriptorProto: msgDesc,
+	}
+	nsg := &descriptor.Message{
+		DescriptorProto: nestDesc,
+	}
+	msg.Fields = []*descriptor.Field{
+		{
+			Message:              msg,
+			FieldDescriptorProto: msg.GetField()[0],
+		},
+	}
+	nsg.Fields = []*descriptor.Field{
+		{
+			Message:              nsg,
+			FieldDescriptorProto: nsg.GetField()[0],
+		},
+	}
+	file := descriptor.File{
+		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+			Name:           proto.String("example.proto"),
+			Package:        proto.String("example"),
+			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
+			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+			Options: &descriptorpb.FileOptions{
+				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+			},
+		},
+		GoPkg: descriptor.GoPackage{
+			Path: "example.com/path/to/example/example.pb",
+			Name: "example_pb",
+		},
+		Messages: []*descriptor.Message{msg, nsg},
+		Services: []*descriptor.Service{
+			{
+				ServiceDescriptorProto: svc,
+				Methods: []*descriptor.Method{
+					{
+						MethodDescriptorProto: meth,
+						RequestType:           msg,
+						ResponseType:          nsg,
+						Bindings: []*descriptor.Binding{
+							{
+								HTTPMethod: "POST",
+								Body: &descriptor.Body{
+									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+								},
+								PathTmpl: httprule.Template{
+									Version:  1,
+									OpCodes:  []int{0, 0},
+									Template: "/v1/echo",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	reg := descriptor.NewRegistry()
+	reg.SetUseJSONNamesForFields(true)
+	// Enable the flag to omit type when $ref is present
+	reg.SetOmitArrayItemTypeWhenRefSibling(true)
+	if err := AddErrorDefs(reg); err != nil {
+		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+		return
+	}
+	fileCL := crossLinkFixture(&file)
+	if err := reg.Load(&pluginpb.CodeGeneratorRequest{
+		ProtoFile: []*descriptorpb.FileDescriptorProto{
+			{
+				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+				Name:           proto.String("acme/example.proto"),
+				Package:        proto.String("example"),
+				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
+				Service:        []*descriptorpb.ServiceDescriptorProto{},
+				Options: &descriptorpb.FileOptions{
+					GoPackage: proto.String("acme/example"),
+				},
+			},
+		},
+	}); err != nil {
+		t.Errorf("reg.Load(%#v) failed with %v; want success", reg, err)
+		return
+	}
+	expect := openapiDefinitionsObject{
+		"rpcStatus": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "code",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type:   "integer",
+							Format: "int32",
+						},
+					},
+				},
+				keyVal{
+					Key: "message",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "string",
+						},
+					},
+				},
+				keyVal{
+					Key: "details",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/protobufAny",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"exampleExampleMessage": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "children",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/exampleExampleMessage",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"exampleNestDescMessage": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "children",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/exampleExampleMessage",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"protobufAny": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "@type",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "string",
+						},
+					},
+				},
+			},
+			AdditionalProperties: &openapiSchemaObject{},
+		},
+	}
+
+	result, err := applyTemplate(param{File: fileCL, reg: reg})
+	if err != nil {
+		t.Errorf("applyTemplate(%#v) failed with %v; want success", reg, err)
+		return
+	}
+	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+	}
+	if want, is, name := expect, result.Definitions, "Produces"; !reflect.DeepEqual(is, want) {
+		t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
+	}
+	// If there was a failure, print out the input and the json result for debugging.
+	if t.Failed() {
+		t.Errorf("had: %s", file)
+		t.Errorf("got: %s", fmt.Sprint(result))
+	}
+}
+
 func TestQueryParameterType(t *testing.T) {
 	ntDesc := &descriptorpb.DescriptorProto{
 		Name: proto.String("AddressEntry"),