From f8084fd51098801ed15320b900db68849c6f8393 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 9c36a763fb6d21d9d3f65d7d55639a616e339386 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 dc9939a82a95b164948a4f225fefc217cfd734f4 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 f85eaecf78..75a4cdc859 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Add document lifecycle guide and runnable sample ([#2017](https://github.com/opensearch-project/opensearch-java/pull/2017))
### Fixed
+- 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