jd-opensource/joyagent-jdgenie · error · IllegalArgumentException

Input must be a Map

Error message

Input must be a Map

What it means

PlanningTool.execute requires the input to be a Map of parameters; any other type (String, List, null) throws IllegalArgumentException. It enforces the tool's parameter contract before dispatching to a command handler.

Solutions

  1. Parse the input JSON into a Map before calling execute
  2. Check instanceof Map before invocation
  3. Ensure the caller serializes tool arguments as an object, not a string

Example fix

// before
String out = planningTool.execute(rawJsonString);
// after
Map<String,Object> params = objectMapper.readValue(rawJsonString, new TypeReference<Map<String,Object>>(){});
String out = planningTool.execute(params);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(input instanceof Map)) { throw new IllegalArgumentException("expected Map"); }

Type guard

boolean isParamMap(Object o) { return o instanceof Map; }

Try / catch

try {
    return planningTool.execute(input);
} catch (IllegalArgumentException e) {
    input = objectMapper.readValue(String.valueOf(input), Map.class);
    return planningTool.execute(input);
}

Prevention

When it happens

Trigger: Calling PlanningTool.execute with a raw JSON string, a list, or null instead of a Map<String,Object> of parameters.

Common situations: Passing the unparsed JSON body directly to execute, framework deserializing arguments differently than expected, or programmatic misuse of the tool API.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at genie-backend/src/main/java/com/jd/genie/agent/tool/common/PlanningTool.java:122

    private Map<String, Object> getStepStatusProperty() {
        Map<String, Object> stepStatus = new HashMap<>();
        stepStatus.put("type", "string");
        stepStatus.put("enum", Arrays.asList("not_started", "in_progress", "completed", "blocked"));
        stepStatus.put("description", "Status to set for a step. Used with mark_step command.");
        return stepStatus;
    }

    private Map<String, Object> getStepNotesProperty() {
        Map<String, Object> stepNotes = new HashMap<>();
        stepNotes.put("type", "string");
        stepNotes.put("description", "Additional notes for a step. Optional for mark_step command.");
        return stepNotes;
    }

    @Override
    public Object execute(Object input) {
        if (!(input instanceof Map)) {
            throw new IllegalArgumentException("Input must be a Map");
        }

        Map<String, Object> params = (Map<String, Object>) input;
        String command = (String) params.get("command");

        if (command == null || command.isEmpty()) {
            throw new IllegalArgumentException("Command is required");
        }

        Function<Map<String, Object>, String> handler = commandHandlers.get(command);
        if (handler != null) {
            return handler.apply(params);
        } else {
            throw new IllegalArgumentException("Unknown command: " + command);
        }
    }

    private String createPlan(Map<String, Object> params) {

View on GitHub (pinned to 2417e0b8b6)