Error on connecting external connector and embedding model

Versions: OpenSearch 2.19 (AWS managed servers)

Describe the issue:

I’ve configured a connector (that goes on company’s internal API embedding model) and a model to use it. Running a predict on model works just fine (got back the “inference_results”, the embedding vector, etc).

Now I’m trying to create a pipeline in order to ingest a document and store its embedded data, as it’s supposed to be integrated with the model and connector.

The issue is that putting a new doc on a index, using this pipeline, give me back the error below:

PUT /index-01/_doc/1
{
“text”: “Hello world”,
“id”: “s1”
}
{
“error”: {
“root_cause”: [
{
“type”: “illegal_state_exception”,
“reason”: “failed while calling model, check error log for details”
}
],
“type”: “illegal_state_exception”,
“reason”: “failed while calling model, check error log for details”
},
“status”: 500
}

Please, help me understanding what is the issue and how to fix it.

I don’t see how the “input” parameter on connector is linked to the index “text” field. I can’t find on official docs what should I do.

I also don’t know which log is it talking about. I’ve log groups configurated on CloudWatch, but couldn’t find any regarding this issue. Where can I find it?

Am I missing something? What should I change?

Configuration:

#################

CONNECTOR

#################

GET /_plugins/_ml/connectors/zQTjfpoBTaRJSyt75N7r
{
“name”: “LiteLLM embeddings - text-embedding-3-small”,
“version”: “1”,
“description”: “LiteLLM embeddings - text-embedding-3-small”,
“protocol”: “http”,
“parameters”: {
“endpoint”: “``api.example.com``”,
“model”: “text-embedding-3-small”
},
“actions”: [
{
“action_type”: “PREDICT”,
“method”: “POST”,
“url”: “``https://$``{parameters.endpoint}/ia/texto/v1/litellm/litellm/engines/${parameters.model}/embeddings”,
“headers”: {
“api-key”: “${credential.secretArn.api-key}”,
“Host”: “``api.example.com``”,
“Content-Type”: “application/json”
},
“request_body”: “”“{ “input”: ${parameters.input} }”“”
}
],
“owner”: {
“name”: “xxx”,
“backend_roles”: [
“xxx”
],
“roles”: [
“manage_snapshots”,
“ml_full_access”,
“snapshot_management_read_access”
],
“custom_attribute_names”: ,
“user_requested_tenant”: “null”,
“user_requested_tenant_access”: “NONE”
},
“access”: “public”,
“created_time”: 1763065455850,
“last_updated_time”: 1763065455850,
“client_config”: {
“max_connection”: 30,
“connection_timeout”: 30000,
“read_timeout”: 30000,
“retry_backoff_millis”: 200,
“retry_timeout_seconds”: 30,
“max_retry_times”: 0,
“retry_backoff_policy”: “constant”,
“verify_ssl”: false
}
}

#################

MODEL GROUP

#################

GET /_plugins/_ml/model_groups/URXlfpoBMnrhtYk0Va3l
{
“name”: “LiteLLM embeddings - text-embedding-3-small”,
“latest_version”: 2,
“description”: “LiteLLM embeddings - text-embedding-3-small”,
“backend_roles”: [
“xxx”
],
“owner”: {
“name”: “xxx”,
“backend_roles”: ,
“roles”: [
“security_manager”,
“all_access”
],
“custom_attribute_names”: ,
“user_requested_tenant”: null,
“user_requested_tenant_access”: “WRITE”
},
“access”: “restricted”,
“created_time”: 1763065550309,
“last_updated_time”: 1763066083432
}

#################

MODEL

#################

GET /_plugins/_ml/models/0hXrfpoBMnrhtYk0lcXw
{
“name”: “LiteLLM embeddings - text-embedding-3-small”,
“model_group_id”: “URXlfpoBMnrhtYk0Va3l”,
“algorithm”: “REMOTE”,
“model_version”: “1”,
“description”: “LiteLLM embeddings - text-embedding-3-small”,
“model_state”: “DEPLOYED”,
“created_time”: 1763065959920,
“last_updated_time”: 1763066030159,
“last_deployed_time”: 1763066030159,
“auto_redeploy_retry_times”: 0,
“planning_worker_node_count”: 3,
“current_worker_node_count”: 3,
“planning_worker_nodes”: [
“WYAJWdCOTpqTP2zF4LjzhA”,
“Lgkv7fYeTou7zX0mtAoJMQ”,
“NzhBKp-VTLW99scG79875A”
],
“deploy_to_all_nodes”: true,
“is_hidden”: false,
“connector_id”: “zQTjfpoBTaRJSyt75N7r”
}

Validating connector:

POST /_plugins/_ml/models/0hXrfpoBMnrhtYk0lcXw/_predict
{
“parameters”: {
“input”: [“Hello world”]
}
}
{
“inference_results”: [
{
“output”: [
{
“name”: “response”,
“dataAsMap”: {
“model”: “text-embedding-3-small”,
“data”: [
{
“embedding”: [
-0.002078542485833168,
-0.04908587411046028,
[…]
-0.006101156119257212
],
“index”: 0,
“object”: “embedding”
}
],
“object”: “list”,
“usage”: {
“completion_tokens”: 0,
“prompt_tokens”: 2,
“total_tokens”: 2
}
}
}
],
“status_code”: 200
}
]
}

#################

PIPELINE

#################

GET _ingest/pipeline/api_text-embedding-3-small_field_text
{
“api_text-embedding-3-small_field_text”: {
“description”: “LiteLLM embeddings - text-embedding-3-small”,
“processors”: [
{
“text_embedding”: {
“model_id”: “0hXrfpoBMnrhtYk0lcXw”,
“field_map”: {
“text”: “passage_embedding”
}
}
}
]
}
}

#################

INDEX

#################

GET index-01
{
“index-01”: {
“aliases”: {},
“mappings”: {
“properties”: {
“id”: {
“type”: “text”
},
“passage_embedding”: {
“type”: “knn_vector”,
“dimension”: 3072,
“method”: {
“engine”: “lucene”,
“space_type”: “l2”,
“name”: “hnsw”,
“parameters”: {}
}
},
“text”: {
“type”: “text”
}
}
},
“settings”: {
“index”: {
“replication”: {
“type”: “DOCUMENT”
},
“number_of_shards”: “1”,
“provided_name”: “index-01”,
“default_pipeline”: “api_text-embedding-3-small_field_text”,
“knn”: “true”,
“creation_date”: “1763037729696”,
“number_of_replicas”: “1”,
“uuid”: “XU2HJSpsQZa4lGpnIjivNg”,
“version”: {
“created”: “136407827”
}
}
}
}
}

Getting error when inserting data:

PUT /index-01/_doc/1
{
“text”: “Hello world”,
“id”: “s1”
}
{
“error”: {
“root_cause”: [
{
“type”: “illegal_state_exception”,
“reason”: “failed while calling model, check error log for details”
}
],
“type”: “illegal_state_exception”,
“reason”: “failed while calling model, check error log for details”
},
“status”: 500
}

@joaoolavo This is my working example.
I used this document.

POST /_plugins/_ml/connectors/_create
{
  "name": "myopenai",
  "description": "myopenai",
  "version": "1.0",
  "protocol": "http",
  "parameters": {
    "model": "text-embedding-3-small"
  },
  "credential": {
    "openAI_key": "sk-..."
  },
  "actions": [
    {
      "action_type": "predict",
      "method": "POST",
      "url": "https://api.openai.com/v1/embeddings",
      "headers": {
        "Authorization": "Bearer ${credential.openAI_key}"
      },
      "request_body": "{ \"input\": ${parameters.input}, \"model\": \"${parameters.model}\" }",
      "pre_process_function": "connector.pre_process.openai.embedding",
      "post_process_function": "connector.post_process.openai.embedding"
    }
  ]
}

POST /_plugins/_ml/model_groups/_register
{
    "name": "remote_model_group",
    "description": "This is an example description"
}

POST /_plugins/_ml/models/_register
{
  "name": "OpenAI embedding model",
  "function_name": "remote",
  "model_group_id": "ec3BhJoBLdsWuLU2km91",
  "description": "test model",
  "connector_id": "SM20hJoBLdsWuLU2am8n"
}

POST /_plugins/_ml/models/fs3ChJoBLdsWuLU2KG8l/_deploy

POST /_plugins/_ml/models/fs3ChJoBLdsWuLU2KG8l/_predict
{
  "parameters": {
    "input": [ "What is the meaning of life?" ]
  }
}


PUT /_ingest/pipeline/nlp-ingest-pipeline
{
  "description": "A text embedding pipeline",
  "processors": [
    {
      "text_embedding": {
        "model_id": "fs3ChJoBLdsWuLU2KG8l",
        "field_map": {
          "passage_text": "passage_embedding"
        }
      }
    }
  ]
}

PUT /index-01
{
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "knn": true,
      "replication": {
        "type": "DOCUMENT"
      },
      "default_pipeline": "nlp-ingest-pipeline"
    }
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "text"
      },
      "passage_embedding": {
        "type": "knn_vector",
        "dimension": 1536,
        "method": {
          "engine": "lucene",
          "space_type": "l2",
          "name": "hnsw",
          "parameters": {}
        }
      },
      "text": {
        "type": "text"
      }
    }
  }
}

PUT /index-01/_doc/1
{
  "passage_text": "Hello world",
  "id": "s1"
}
GET index-01/_search

{
  "took": 16,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "index-01",
        "_id": "1",
        "_score": 1,
        "_source": {
          "passage_text": "Hello world",
          "passage_embedding": [
            -0.0020785425,
            -0.049085874,
            0.02094679,
            0.031351026,
            -0.045305308,
            -0.026402483,
            -0.028999701,
            0.060304623,
            -0.025710916,

@joaoolavo One more thing. 3072 dimension is for large model.
Since you’re using small model, you must use 1536 in the index definition.

@pablo thank you for your reply.

I can’t find any relevant difference between your steps and mine.

The thing that I don’t understand is the “magic trick” that makes the content of the field “passage_text” be passed to the ${parameters.input} parameter on the connector.

You specify the ‘“input”: ${parameters.input}’, you even use it on the predict model test, but I don’t see what’s the connection between this parameter and the “passage_text” parameter when putting a new doc.

If you have any other suggesting, please, let me know!

PS: You are right, the correct model dimension is also 1536, I already fixed it, but the same issue persistis.

@joaoolavo Do you still get error 500? Do you get anything in OpenSearch logs?

@joaoolavo Regarding the connection. The Input and passage text are connected through pipeline. When you create an index you use the pipeline in the settings which already have mapping to the passage_text and the model.

Ok, I did it! :smiley:

@pablo Sorry, I missed it, I see now that you said that at first.

The solution is to add the following line when creating the connector:

            "post_process_function": "connector.post_process.openai.embedding"

As below:

[...]
    "actions": [
        {
            "headers": {
                "api-key": "${credential.secretArn.api-key}",
                "Host": api.example.com,
                "Content-Type": "application/json"
            },
            "method": "POST",
            "request_body": "{ \"input\": ${parameters.input} }",
            "action_type": "PREDICT",
            "url": "https://${parameters.endpoint}/ia/texto/v1/litellm/litellm/engines/${parameters.model}/embeddings",
            "post_process_function": "connector.post_process.openai.embedding" // ADD THIS TO MAKE IT WORK
        }
    ]
[...]

It seems that, when using a remote model connector with OpenAI-compatible API (LiteLLM in my case) for text embeddings, the text_embedding processor in an ingest pipeline was failing with:

“org.opensearch.neuralsearch.ml.MLCommonsClientAccessor.buildVectorFromResponse(MLCommonsClientAccessor.java:251)“

[2025-11-18T17:08:15,694][WARN ][r.suppressed             ] [f0793a6f6c3034c26a916a882cc90276] path: __PATH__ params: {pretty=true, index=index01, id=1}
IngestProcessorException[java.lang.IllegalStateException: failed while calling model, check error log for details]; nested: IllegalStateException[failed while calling model, check error log for details];
	at org.opensearch.ingest.CompoundProcessor.newCompoundProcessorException(CompoundProcessor.java:356)
	at org.opensearch.ingest.CompoundProcessor.lambda$innerExecute$4(CompoundProcessor.java:278)
	at org.opensearch.neuralsearch.processor.TextEmbeddingProcessor.lambda$doExecute$1(TextEmbeddingProcessor.java:53)
	at org.opensearch.core.action.ActionListener$1.onFailure(ActionListener.java:90)
	at org.opensearch.neuralsearch.util.RetryUtil.handleRetryOrFailure(RetryUtil.java:48)
	at org.opensearch.neuralsearch.ml.MLCommonsClientAccessor.lambda$retryableInferenceSentencesWithVectorResult$6(MLCommonsClientAccessor.java:192)
	at org.opensearch.core.action.ActionListener$1.onFailure(ActionListener.java:90)
	at org.opensearch.core.action.ActionListener$1.onResponse(ActionListener.java:84)
	at org.opensearch.ml.client.MachineLearningNodeClient.lambda$getMlPredictionTaskResponseActionListener$5(MachineLearningNodeClient.java:378)
	at org.opensearch.core.action.ActionListener$1.onResponse(ActionListener.java:82)
	at org.opensearch.ml.client.MachineLearningNodeClient.lambda$wrapActionListener$6(MachineLearningNodeClient.java:394)
	at org.opensearch.core.action.ActionListener$1.onResponse(ActionListener.java:82)
	at org.opensearch.action.support.TransportAction$1.onResponse(TransportAction.java:115)
	at org.opensearch.action.support.TransportAction$1.onResponse(TransportAction.java:109)
	at org.opensearch.core.action.ActionListener$6.onResponse(ActionListener.java:301)
	at org.opensearch.core.action.ActionListener$5.onResponse(ActionListener.java:268)
	at org.opensearch.action.ActionListenerResponseHandler.handleResponse(ActionListenerResponseHandler.java:70)
	at org.opensearch.security.transport.SecurityInterceptor$RestoringTransportResponseHandler.handleResponse(SecurityInterceptor.java:441)
	at org.opensearch.transport.TransportService$ContextRestoreResponseHandler.handleResponse(TransportService.java:1517)
	at org.opensearch.transport.NativeMessageHandler.doHandleResponse(NativeMessageHandler.java:427)
	at org.opensearch.transport.NativeMessageHandler.handleResponse(NativeMessageHandler.java:419)
	at org.opensearch.transport.NativeMessageHandler.handleMessage(NativeMessageHandler.java:174)
	at org.opensearch.transport.NativeMessageHandler.messageReceived(NativeMessageHandler.java:126)
	at org.opensearch.transport.InboundHandler.messageReceivedFromPipeline(InboundHandler.java:120)
	at org.opensearch.transport.InboundHandler.inboundMessage(InboundHandler.java:112)
	at org.opensearch.transport.TcpTransport.inboundMessage(TcpTransport.java:768)
	at org.opensearch.transport.InboundBytesHandler.forwardFragments(InboundBytesHandler.java:137)
	at org.opensearch.transport.InboundBytesHandler.doHandleBytes(InboundBytesHandler.java:77)
	at org.opensearch.transport.InboundPipeline.doHandleBytes(InboundPipeline.java:124)
	at org.opensearch.transport.InboundPipeline.handleBytes(InboundPipeline.java:113)
	at org.opensearch.transport.netty4.Netty4MessageChannelHandler.channelRead(Netty4MessageChannelHandler.java:95)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:280)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1515)
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1378)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1427)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:530)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:469)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1357)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:868)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:796)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:697)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:660)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:998)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at __PATH__(Thread.java:1583)
Caused by: java.lang.IllegalStateException: failed while calling model, check error log for details
	at org.opensearch.neuralsearch.ml.MLCommonsClientAccessor.buildVectorFromResponse(MLCommonsClientAccessor.java:251)
	at org.opensearch.neuralsearch.ml.MLCommonsClientAccessor.lambda$retryableInferenceSentencesWithVectorResult$4(MLCommonsClientAccessor.java:189)
	at org.opensearch.core.action.ActionListener$1.onResponse(ActionListener.java:82)
	... 51 more

(I was able to see this error on CloudWatch > OpenSearch error log group)

As explained by an AI chat, the OpenAI API returns embeddings in this format:

{
  "data": [
    {
      "embedding": [0.1, 0.2, 0.3, ...],
      "index": 0,
      "object": "embedding"
    }
  ],
  "object": "list"
}

The text_embedding processor expects the vector to be directly accessible in the response structure, but without post-processing, it was wrapped in dataAsMap.data[0].embedding, making it impossible for the processor to extract the vector array.

And it explained that adding the post_process_function parameter to the connector configuration would fix it.

“This built-in function transforms the OpenAI response format into the structure expected by the text_embedding processor, extracting the embedding array from data[0].embedding and making it directly accessible.”

So, now I can insert a doc and save its embedded vector:

PUT /index-01/_doc/1
{
“text”: “Hello world”,
“id”: “s2”
}
GET /index-01/_doc/1

{
“_index”: “index-01”,
“_id”: “1”,
“_version”: 1,
“_seq_no”: 0,
“_primary_term”: 1,
“found”: true,
“_source”: {
“passage_embedding”: [
-0.0020785425,
-0.049085874,
[…]
-0.013985017,
-0.006101156
],
“text”: “Hello world”,
“id”: “s2”
}
}

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.