conductor-oss/conductor · error · RuntimeException
Embeddings API call failed:
Error message
Embeddings API call failed:
What it means
OpenAI.java wraps an IOException from OpenAIEmbeddingsApi.createEmbeddings() in a RuntimeException with the message "Embeddings API call failed: " + underlying message. The underlying IOException is thrown by OpenAIEmbeddingsApi when the HTTP response is non-2xx (error 217) or when the OkHttp call itself fails (DNS, timeout, connection refused). This wrapper preserves the cause chain via the second constructor argument.
Source
Thrown at ai/src/main/java/org/conductoross/conductor/ai/providers/openai/OpenAI.java:110
public String getModelProvider() {
return NAME;
}
@Override
public List<Float> generateEmbeddings(EmbeddingGenRequest embeddingGenRequest) {
try {
var request =
new OpenAIEmbeddingsApi.EmbeddingRequest(
embeddingGenRequest.getModel(),
embeddingGenRequest.getText(),
embeddingGenRequest.getDimensions());
var result = embeddingsApi.createEmbeddings(request);
if (result.data() != null && !result.data().isEmpty()) {
return result.data().getFirst().embedding();
}
return List.of();
} catch (IOException e) {
throw new RuntimeException("Embeddings API call failed: " + e.getMessage(), e);
}
}
@Override
public ChatOptions getChatOptions(ChatCompletion input) {
List<Tool> tools = convertTools(input);
OpenAIResponsesChatOptions.OpenAIResponsesChatOptionsBuilder builder =
OpenAIResponsesChatOptions.builder()
.model(input.getModel())
.topP(input.getTopP())
.frequencyPenalty(input.getFrequencyPenalty())
.presencePenalty(input.getPresencePenalty())
.maxTokens(input.getMaxTokens())
.stopSequences(input.getStopWords())
.previousResponseId(input.getPreviousResponseId())
.reasoningEffort(input.getReasoningEffort())
.reasoningSummary(input.getReasoningSummary())View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Check the wrapped cause (getCause()) — it contains the HTTP status and response body from the API (e.g. 401 Unauthorized, 429 rate-limited).
- Verify the embedding model name matches an OpenAI embeddings model (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002).
- Verify the API key is valid and the baseURL ends with /v1 (the OpenAI constructor normalizes this, but check if using a custom OpenAI instance).
- For 429 rate limits, add backoff/retry in the calling workflow task.
Example fix
// before
try {
List<Float> emb = llm.generateEmbeddings(req);
} catch (RuntimeException e) {
log.error("embedding failed", e);
}
// after
try {
List<Float> emb = llm.generateEmbeddings(req);
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException io) {
log.error("Embeddings API IOException: {}", io.getMessage());
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate model name and API key before calling generateEmbeddings()
String model = embeddingGenRequest.getModel();
if (model == null || model.isBlank()) {
throw new IllegalArgumentException("Embedding model name is required");
}
// Validate it's a known embeddings model
Set<String> validEmbeddingModels = Set.of(
"text-embedding-3-small", "text-embedding-3-large", "text-embedding-ada-002");
if (!validEmbeddingModels.contains(model)) {
log.warn("Model '{}' is not in known embeddings model list", model);
} Type guard
null
Try / catch
try {
List<Float> embeddings = llm.generateEmbeddings(request);
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
// Network or HTTP error — check the message for status code
log.error("Embeddings API failed: {}", cause.getMessage());
if (cause.getMessage().contains("429")) {
// Rate limit — retry with backoff
Thread.sleep(backoffMs);
return retry(request);
}
}
throw e;
} Prevention
- Validate the embedding model name is a supported OpenAI embeddings model before calling.
- Check API key validity at startup with a cheap health-check call.
- Implement rate-limit-aware backoff for bulk embedding operations.
- Monitor embedding API response times and error rates.
When it happens
Trigger: generateEmbeddings() is called (via LLMHelper) and the POST /v1/embeddings request fails: invalid/expired API key (401), rate limit (429), wrong model name (404), wrong baseURL (404/connection refused), or network timeout. The IOException from the API client is caught and rethrown as RuntimeException.
Common situations: Expired or revoked API key; embedding model name typo (e.g. "text-embedding-3" instead of "text-embedding-3-small"); baseURL misconfigured without /v1 suffix (handled by ensureV1 but custom configs may bypass); network proxy/firewall blocking api.openai.com; rate limit during bulk indexing.
Related errors
- Embeddings API failed with status %d: %s
- Speech API call failed:
- Image generation API call failed:
- OpenAI Responses API call failed:
- Failed to submit video generation:
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/a6d7b14ea34758f5.
Report an issue: GitHub.