{"record":{"id":"a6d7b14ea34758f5","repo":"conductor-oss/conductor","slug":"embeddings-api-call-failed","errorCode":null,"errorMessage":"Embeddings API call failed: ","messagePattern":"Embeddings API call failed: ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/providers/openai/OpenAI.java","lineNumber":110,"sourceCode":"    public String getModelProvider() {\n        return NAME;\n    }\n\n    @Override\n    public List<Float> generateEmbeddings(EmbeddingGenRequest embeddingGenRequest) {\n        try {\n            var request =\n                    new OpenAIEmbeddingsApi.EmbeddingRequest(\n                            embeddingGenRequest.getModel(),\n                            embeddingGenRequest.getText(),\n                            embeddingGenRequest.getDimensions());\n            var result = embeddingsApi.createEmbeddings(request);\n            if (result.data() != null && !result.data().isEmpty()) {\n                return result.data().getFirst().embedding();\n            }\n            return List.of();\n        } catch (IOException e) {\n            throw new RuntimeException(\"Embeddings API call failed: \" + e.getMessage(), e);\n        }\n    }\n\n    @Override\n    public ChatOptions getChatOptions(ChatCompletion input) {\n        List<Tool> tools = convertTools(input);\n\n        OpenAIResponsesChatOptions.OpenAIResponsesChatOptionsBuilder builder =\n                OpenAIResponsesChatOptions.builder()\n                        .model(input.getModel())\n                        .topP(input.getTopP())\n                        .frequencyPenalty(input.getFrequencyPenalty())\n                        .presencePenalty(input.getPresencePenalty())\n                        .maxTokens(input.getMaxTokens())\n                        .stopSequences(input.getStopWords())\n                        .previousResponseId(input.getPreviousResponseId())\n                        .reasoningEffort(input.getReasoningEffort())\n                        .reasoningSummary(input.getReasoningSummary())","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/providers/openai/OpenAI.java#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntry {\n    List<Float> emb = llm.generateEmbeddings(req);\n} catch (RuntimeException e) {\n    log.error(\"embedding failed\", e);\n}\n// after\ntry {\n    List<Float> emb = llm.generateEmbeddings(req);\n} catch (RuntimeException e) {\n    Throwable cause = e.getCause();\n    if (cause instanceof IOException io) {\n        log.error(\"Embeddings API IOException: {}\", io.getMessage());\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Validate model name and API key before calling generateEmbeddings()\nString model = embeddingGenRequest.getModel();\nif (model == null || model.isBlank()) {\n    throw new IllegalArgumentException(\"Embedding model name is required\");\n}\n// Validate it's a known embeddings model\nSet<String> validEmbeddingModels = Set.of(\n    \"text-embedding-3-small\", \"text-embedding-3-large\", \"text-embedding-ada-002\");\nif (!validEmbeddingModels.contains(model)) {\n    log.warn(\"Model '{}' is not in known embeddings model list\", model);\n}","typeGuard":"null","tryCatchPattern":"try {\n    List<Float> embeddings = llm.generateEmbeddings(request);\n} catch (RuntimeException e) {\n    Throwable cause = e.getCause();\n    if (cause instanceof IOException) {\n        // Network or HTTP error — check the message for status code\n        log.error(\"Embeddings API failed: {}\", cause.getMessage());\n        if (cause.getMessage().contains(\"429\")) {\n            // Rate limit — retry with backoff\n            Thread.sleep(backoffMs);\n            return retry(request);\n        }\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["openai","embeddings","network","api-error","ai"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}