jd-opensource/joyagent-jdgenie · error · IllegalArgumentException
Invalid or empty response from LLM
Error message
Invalid or empty response from LLM
What it means
LLM.askTool validates the completion response and throws IllegalArgumentException when choices is null/empty or the first choice has no message object. Unlike ask, it tolerates null content (tool calls may have no text) but requires a message node.
Solutions
- Log the raw responseJson included in the error path to diagnose
- Verify tool schemas and model support for function calling
- Check HTTP status and error body before parsing choices
- Retry transient failures with backoff
Example fix
// before
LLMToolResponse r = llm.askTool(msgs, "auto", tools);
// after
try {
LLMToolResponse r = llm.askTool(msgs, "auto", tools);
} catch (IllegalArgumentException e) {
log.error("invalid llm tool response, check model/endpoint");
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// check HTTP status and that body parses as JSON before parsing choices
Try / catch
try {
return llm.askTool(msgs, choice, tools);
} catch (IllegalArgumentException e) {
log.error("invalid llm tool response, retrying", e);
return retryWithBackoff(() -> llm.askTool(msgs, choice, tools));
} Prevention
- Confirm the model supports function calling
- Validate tool JSON schemas against provider spec
- Monitor rate limits and error payloads
- Log responseJson for diagnosis
When it happens
Trigger: Calling askTool when the endpoint returns an error payload, empty body, truncated stream, or a choices[0] without a message field.
Common situations: Model/provider rejects the tool schema and returns an error JSON, quota/rate-limit responses, wrong endpoint path, or incompatible API version that reshapes the response.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Empty or invalid response from LLM
- 解析llm json结果失败
- Invalid tool_choice: " + toolChoice
- Error in generating model output
- Error in code parsing
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/7c54d63dd95b4e3a.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/agent/llm/LLM.java:480
if (Objects.nonNull(extParams)) {
params.putAll(extParams);
}
log.info("{} call llm request {}", context.getRequestId(), JSONObject.toJSONString(params));
if (!stream) {
params.put("stream", false);
// 调用 API
CompletableFuture<String> future = callOpenAI(params, timeout);
return future.thenApply(responseJson -> {
try {
// 解析响应
log.info("{} call llm response {}", context.getRequestId(), responseJson);
JsonNode jsonResponse = objectMapper.readTree(responseJson);
JsonNode choices = jsonResponse.get("choices");
if (choices == null || choices.isEmpty() || choices.get(0).get("message") == null) {
log.error("{} Invalid response: {}", context.getRequestId(), responseJson);
throw new IllegalArgumentException("Invalid or empty response from LLM");
}
// 提取响应内容
JsonNode message = choices.get(0).get("message");
String content = message.has("content") && !"null".equals(message.get("content").asText()) ? message.get("content").asText() : null;
// 提取工具调用
List<ToolCall> toolCalls = new ArrayList<>();
if ("struct_parse".equals(functionCallType)) {
// 匹配方式: 直接匹配 ```json ... ``` 代码块
String pattern = "```json\\s*([\\s\\S]*?)\\s*```";
List<String> matches = findMatches(content, pattern);
if (!matches.isEmpty()) {
for (String match : matches) {
ToolCall oneToolCall = parseToolCall(context, match);
if (Objects.nonNull(oneToolCall)) {
toolCalls.add(oneToolCall);
}View on GitHub (pinned to 2417e0b8b6)