jd-opensource/joyagent-jdgenie · error · IllegalArgumentException

Empty or invalid response from LLM

Error message

Empty or invalid response from LLM

What it means

LLM.ask parses the chat-completion HTTP response and throws IllegalArgumentException when the JSON has no choices array, an empty choices array, or the first choice's message.content is null. It guards the contract that a successful completion always contains text content.

Solutions

  1. Log/inspect the raw response to see what was actually returned
  2. Verify the model name, endpoint URL, and API version are compatible
  3. Handle non-200 responses before parsing choices
  4. Add retry with backoff for transient empty responses

Example fix

// before
return llm.ask(messages);
// after
try {
    return llm.ask(messages);
} catch (IllegalArgumentException e) {
    log.error("llm empty response, retrying");
    return llm.ask(messages);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate HTTP 200 and non-empty body before calling ask

Try / catch

try {
    String content = llm.ask(messages);
} catch (IllegalArgumentException e) {
    log.error("empty llm response");
    throw new RetryableException(e);
}

Prevention

When it happens

Trigger: Calling LLM.ask when the LLM endpoint returns a malformed/empty body, an error JSON without choices, a truncated response, or a choice whose message has no content field.

Common situations: Wrong API base URL or incompatible endpoint, model returns a refusal/error payload, content-filtered responses, proxy returning an HTML error page, or API version change altering the response schema.

Related errors


AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08). Data as JSON: /api/errors/72ebe4f18b92a880. Report an issue: GitHub.

Appendix: source

Thrown at genie-backend/src/main/java/com/jd/genie/agent/llm/LLM.java:255

            }

            log.info("{} call llm ask request {}", context.getRequestId(), JSONObject.toJSONString(params));
            // 处理非流式请求
            if (!stream) {
                params.put("stream", false);

                // 调用 API
                CompletableFuture<String> future = callOpenAI(params);

                return future.thenApply(response -> {
                    try {
                        // 解析响应
                        log.info("{} call llm response {}", context.getRequestId(), response);
                        JsonNode jsonResponse = objectMapper.readTree(response);
                        JsonNode choices = jsonResponse.get("choices");

                        if (choices == null || choices.isEmpty() || choices.get(0).get("message").get("content") == null) {
                            throw new IllegalArgumentException("Empty or invalid response from LLM");
                        }

                        return choices.get(0).get("message").get("content").asText();
                    } catch (IOException e) {
                        throw new CompletionException(e);
                    }
                });
            } else {
                // 处理流式请求
                params.put("stream", true);
                // 调用流式 API
                return callOpenAIStream(params);
            }
        } catch (Exception e) {
            log.error("{} Unexpected error in ask: {}", e.getMessage(), e);
            CompletableFuture<String> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;

View on GitHub (pinned to 2417e0b8b6)