jd-opensource/joyagent-jdgenie · error · IllegalArgumentException

Invalid tool_choice: " + toolChoice

Error message

Invalid tool_choice: " + toolChoice

What it means

LLM.askTool validates the toolChoice parameter against ToolChoice.isValid before issuing the request and throws IllegalArgumentException for unrecognized values. toolChoice must be one of the enum's supported values (e.g. auto/none/required or a specific tool name form).

Solutions

  1. Use ToolChoice enum constants instead of raw strings
  2. Validate with ToolChoice.isValid(value) before calling askTool
  3. Fix typos in configured toolChoice values

Example fix

// before
llm.askTool(messages, "AUTO", tools, ...);
// after
llm.askTool(messages, ToolChoice.AUTO.name(), tools, ...);
Defensive patterns

Strategy: validation

Validate before calling

if (!ToolChoice.isValid(toolChoice)) { throw new IllegalArgumentException("bad toolChoice: " + toolChoice); }

Type guard

boolean validChoice(String c) { return ToolChoice.isValid(c); }

Try / catch

try {
    return llm.askTool(msgs, toolChoice, tools, ...);
} catch (IllegalArgumentException e) {
    return llm.askTool(msgs, ToolChoice.AUTO.name(), tools, ...);
}

Prevention

When it happens

Trigger: Calling askTool with a toolChoice string that is null, misspelled, or not a registered ToolChoice value.

Common situations: Hardcoded toolChoice from config with a typo, migrating from another SDK that used different tool_choice vocabulary, or building the value dynamically from user input.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

    }

    /**
     * 向 LLM 发送工具请求并获取响应
     */
    public CompletableFuture<ToolCallResponse> askTool(
            AgentContext context,
            List<Message> messages,
            Message systemMsgs,
            ToolCollection tools,
            ToolChoice toolChoice,
            Double temperature,
            boolean stream,
            int timeout
    ) {
        try {
            // 验证 toolChoice
            if (!ToolChoice.isValid(toolChoice)) {
                throw new IllegalArgumentException("Invalid tool_choice: " + toolChoice);
            }
            long startTime = System.currentTimeMillis();

            // 设置 API 请求
            Map<String, Object> params = new HashMap<>();

            // tools
            StringBuilder stringBuilder = new StringBuilder();
            List<Map<String, Object>> formattedTools = new ArrayList<>();
            if ("struct_parse".equals(functionCallType)) {
                GenieConfig genieConfig = SpringContextHolder.getApplicationContext().getBean(GenieConfig.class);
                stringBuilder.append(genieConfig.getStructParseToolSystemPrompt());
                // base tool
                for (BaseTool tool : tools.getToolMap().values()) {
                    Map<String, Object> functionMap = new HashMap<>();
                    functionMap.put("name", tool.getName());
                    functionMap.put("description", tool.getDescription());
                    functionMap.put("parameters", addFunctionNameParam(tool.toParams(), tool.getName()));

View on GitHub (pinned to 2417e0b8b6)