From 11e332c2577eff172a8a909e8e78b7607640a773 Mon Sep 17 00:00:00 2001
From: Radhakrishnan Pachyappan
Date: Sun, 5 Jul 2026 14:18:28 +0530
Subject: [PATCH 1/3] Fix ShardProfile.fetch typed as single FetchProfile
instead of List
OpenSearch returns fetch as a JSON array in profile responses but the
spec defined it as a single FetchProfile object. This caused:
UnexpectedJsonEventException: Unexpected JSON event 'START_ARRAY'
instead of '[START_OBJECT, KEY_NAME]'
Change the spec to type: array / items: FetchProfile and update the
generated ShardProfile.java to use List with array
serialization and arrayDeserializer, matching the pattern already
used by aggregations and searches in the same class.
Fixes #1965
Signed-off-by: Radhakrishnan Pachyappan
---
.../opensearch/core/search/ShardProfile.java | 51 ++++++++++++++-----
java-codegen/opensearch-openapi.yaml | 4 +-
2 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/java-client/src/generated/java/org/opensearch/client/opensearch/core/search/ShardProfile.java b/java-client/src/generated/java/org/opensearch/client/opensearch/core/search/ShardProfile.java
index 8640b1d2d6..15085ab581 100644
--- a/java-client/src/generated/java/org/opensearch/client/opensearch/core/search/ShardProfile.java
+++ b/java-client/src/generated/java/org/opensearch/client/opensearch/core/search/ShardProfile.java
@@ -64,8 +64,8 @@ public class ShardProfile implements PlainJsonSerializable, ToCopyableBuilder aggregations;
- @Nullable
- private final FetchProfile fetch;
+ @Nonnull
+ private final List fetch;
@Nonnull
private final String id;
@@ -77,7 +77,7 @@ public class ShardProfile implements PlainJsonSerializable, ToCopyableBuilder aggregations() {
/**
* API name: {@code fetch}
*/
- @Nullable
- public final FetchProfile fetch() {
+ @Nonnull
+ public final List fetch() {
return this.fetch;
}
@@ -136,9 +136,13 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) {
}
generator.writeEnd();
- if (this.fetch != null) {
+ if (ApiTypeHelper.isDefined(this.fetch)) {
generator.writeKey("fetch");
- this.fetch.serialize(generator, mapper);
+ generator.writeStartArray();
+ for (FetchProfile item0 : this.fetch) {
+ item0.serialize(generator, mapper);
+ }
+ generator.writeEnd();
}
generator.writeKey("id");
@@ -171,7 +175,7 @@ public static Builder builder() {
public static class Builder extends ObjectBuilderBase implements CopyableBuilder {
private List aggregations;
@Nullable
- private FetchProfile fetch;
+ private List fetch;
private String id;
private List searches;
@@ -179,14 +183,14 @@ public Builder() {}
private Builder(ShardProfile o) {
this.aggregations = _listCopy(o.aggregations);
- this.fetch = o.fetch;
+ this.fetch = _listCopy(o.fetch);
this.id = o.id;
this.searches = _listCopy(o.searches);
}
private Builder(Builder o) {
this.aggregations = _listCopy(o.aggregations);
- this.fetch = o.fetch;
+ this.fetch = _listCopy(o.fetch);
this.id = o.id;
this.searches = _listCopy(o.searches);
}
@@ -237,15 +241,36 @@ public final Builder aggregations(Function
+ * Adds all elements of list to fetch.
+ *
+ */
+ @Nonnull
+ public final Builder fetch(List list) {
+ this.fetch = _listAddAll(this.fetch, list);
+ return this;
+ }
+
+ /**
+ * API name: {@code fetch}
+ *
+ *
+ * Adds one or more values to fetch.
+ *
*/
@Nonnull
- public final Builder fetch(@Nullable FetchProfile value) {
- this.fetch = value;
+ public final Builder fetch(FetchProfile value, FetchProfile... values) {
+ this.fetch = _listAdd(this.fetch, value, values);
return this;
}
/**
* API name: {@code fetch}
+ *
+ *
+ * Adds a value to fetch using a builder lambda.
+ *
*/
@Nonnull
public final Builder fetch(Function> fn) {
@@ -325,7 +350,7 @@ public ShardProfile build() {
protected static void setupShardProfileDeserializer(ObjectDeserializer op) {
op.add(Builder::aggregations, JsonpDeserializer.arrayDeserializer(AggregationProfile._DESERIALIZER), "aggregations");
- op.add(Builder::fetch, FetchProfile._DESERIALIZER, "fetch");
+ op.add(Builder::fetch, JsonpDeserializer.arrayDeserializer(FetchProfile._DESERIALIZER), "fetch");
op.add(Builder::id, JsonpDeserializer.stringDeserializer(), "id");
op.add(Builder::searches, JsonpDeserializer.arrayDeserializer(SearchProfile._DESERIALIZER), "searches");
}
diff --git a/java-codegen/opensearch-openapi.yaml b/java-codegen/opensearch-openapi.yaml
index b4829f72a0..468f21a1c0 100644
--- a/java-codegen/opensearch-openapi.yaml
+++ b/java-codegen/opensearch-openapi.yaml
@@ -52512,7 +52512,9 @@ components:
items:
$ref: '#/components/schemas/_core.search___SearchProfile'
fetch:
- $ref: '#/components/schemas/_core.search___FetchProfile'
+ type: array
+ items:
+ $ref: '#/components/schemas/_core.search___FetchProfile'
required:
- aggregations
- id
From b2d36b192bce977a3534eb971e796dd6f09b5539 Mon Sep 17 00:00:00 2001
From: Radhakrishnan Pachyappan
Date: Sun, 5 Jul 2026 14:20:37 +0530
Subject: [PATCH 2/3] Fix NPE in _listAddAll when server returns JSON null for
a list field
_listAddAll(list, values) threw NullPointerException via
Objects.requireNonNull(values) when the incoming values list was null.
This happens when OpenSearch returns JSON null for an optional list
field (e.g. ism_template in ISM Policy), causing deserialization to
fail with an NPE inside the builder.
Guard against null values by returning the existing list unchanged
when values is null, consistent with the intent of treating a missing
or null list as equivalent to an empty one.
Fixes #1813
Signed-off-by: Radhakrishnan Pachyappan
---
.../java/org/opensearch/client/util/ObjectBuilderBase.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/java-client/src/main/java/org/opensearch/client/util/ObjectBuilderBase.java b/java-client/src/main/java/org/opensearch/client/util/ObjectBuilderBase.java
index e1aa0627c9..524b478148 100644
--- a/java-client/src/main/java/org/opensearch/client/util/ObjectBuilderBase.java
+++ b/java-client/src/main/java/org/opensearch/client/util/ObjectBuilderBase.java
@@ -95,10 +95,15 @@ protected static List _listAdd(List list, T value, T... values) {
/** Add all elements of a list to a (possibly {@code null}) list */
protected static List _listAddAll(List list, List values) {
+ if (values == null) {
+ // Server returned JSON null for a list field; treat as empty to avoid NPE.
+ // See https://github.com/opensearch-project/opensearch-java/issues/1813
+ return list;
+ }
if (list == null) {
// Keep the original list to avoid an unnecessary copy.
// It will be copied if we add more values.
- return Objects.requireNonNull(values);
+ return values;
} else {
list = _mutableList(list);
list.addAll(values);
From 2abeeb097e4f9a7ac11f677e2de87890b1d74c84 Mon Sep 17 00:00:00 2001
From: Radhakrishnan Pachyappan
Date: Sun, 5 Jul 2026 15:32:16 +0530
Subject: [PATCH 3/3] Add CHANGELOG entry for #2041
Signed-off-by: Radhakrishnan Pachyappan
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ef4423196..ee38b3dd33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Fixed
- Fix `unitTest` task not running the tests in the `test` source set ([#2074](https://github.com/opensearch-project/opensearch-java/pull/2074))
- Run model tests against both JSON mappers instead of picking one at random ([#2085](https://github.com/opensearch-project/opensearch-java/pull/2085))
+- Fix `NullPointerException` in `_listAddAll` when server returns JSON `null` for a list field ([#2041](https://github.com/opensearch-project/opensearch-java/pull/2041))
## [Unreleased 3.x]
### Added