diff --git a/docs/changelog/122538.yaml b/docs/changelog/122538.yaml new file mode 100644 index 0000000000000..441f454923cbf --- /dev/null +++ b/docs/changelog/122538.yaml @@ -0,0 +1,5 @@ +pr: 122538 +summary: Fix `ArrayIndexOutOfBoundsException` in `ShardBulkInferenceActionFilter` +area: Ingest +type: bug +issues: [] diff --git a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java index 303f957c7ab20..b8e594e70abd9 100644 --- a/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java +++ b/x-pack/plugin/inference/src/internalClusterTest/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilterIT.java @@ -44,6 +44,7 @@ import java.util.Set; import static org.elasticsearch.xpack.inference.mapper.SemanticTextFieldTests.randomSemanticTextInput; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class ShardBulkInferenceActionFilterIT extends ESIntegTestCase { @@ -183,4 +184,48 @@ public void testBulkOperations() throws Exception { searchResponse.decRef(); } } + + public void testItemFailures() { + prepareCreate(INDEX_NAME).setMapping( + String.format( + Locale.ROOT, + """ + { + "properties": { + "sparse_field": { + "type": "semantic_text", + "inference_id": "%s" + }, + "dense_field": { + "type": "semantic_text", + "inference_id": "%s" + } + } + } + """, + TestSparseInferenceServiceExtension.TestInferenceService.NAME, + TestDenseInferenceServiceExtension.TestInferenceService.NAME + ) + ).get(); + + BulkRequestBuilder bulkReqBuilder = client().prepareBulk(); + int totalBulkSize = randomIntBetween(100, 200); // Use a bulk request size large enough to require batching + for (int bulkSize = 0; bulkSize < totalBulkSize; bulkSize++) { + String id = Integer.toString(bulkSize); + + // Set field values that will cause errors when generating inference requests + Map source = new HashMap<>(); + source.put("sparse_field", List.of(Map.of("foo", "bar"), Map.of("baz", "bar"))); + source.put("dense_field", List.of(Map.of("foo", "bar"), Map.of("baz", "bar"))); + + bulkReqBuilder.add(new IndexRequestBuilder(client()).setIndex(INDEX_NAME).setId(id).setSource(source)); + } + + BulkResponse bulkResponse = bulkReqBuilder.get(); + assertThat(bulkResponse.hasFailures(), equalTo(true)); + for (BulkItemResponse bulkItemResponse : bulkResponse.getItems()) { + assertThat(bulkItemResponse.isFailed(), equalTo(true)); + assertThat(bulkItemResponse.getFailureMessage(), containsString("expected [String|Number|Boolean]")); + } + } } diff --git a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilter.java b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilter.java index 3933260664b7c..5fca096dae1e3 100644 --- a/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilter.java +++ b/x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/action/filter/ShardBulkInferenceActionFilter.java @@ -482,7 +482,7 @@ private Map> createFieldInferenceRequests(Bu isUpdateRequest = true; if (updateRequest.script() != null) { addInferenceResponseFailure( - item.id(), + itemIndex, new ElasticsearchStatusException( "Cannot apply update with a script on indices that contain [{}] field(s)", RestStatus.BAD_REQUEST, @@ -540,7 +540,7 @@ private Map> createFieldInferenceRequests(Bu if (valueObj == null || valueObj == EXPLICIT_NULL) { if (isUpdateRequest && useLegacyFormat) { addInferenceResponseFailure( - item.id(), + itemIndex, new ElasticsearchStatusException( "Field [{}] must be specified on an update request to calculate inference for field [{}]", RestStatus.BAD_REQUEST, @@ -557,7 +557,7 @@ private Map> createFieldInferenceRequests(Bu try { values = SemanticTextUtils.nodeStringValues(field, valueObj); } catch (Exception exc) { - addInferenceResponseFailure(item.id(), exc); + addInferenceResponseFailure(itemIndex, exc); break; }