apache/shenyu · error · IllegalArgumentException

Invalid JSON format

Error message

Invalid JSON format: ${e.getMessage()}

What it means

In parseInput, any exception thrown while converting the input string to a JsonObject (Gson syntax error, unexpected token, or the null-result IllegalArgumentException from the same method) is caught, logged, and rethrown as IllegalArgumentException('Invalid JSON format: <cause message>'). This tells the MCP caller its arguments string was not valid JSON.

Solutions

  1. Read the wrapped cause message to see the exact Gson parse error and position.
  2. Fix the client to serialize arguments with a proper JSON library instead of string building.
  3. Validate the arguments string with JSON.parse (or equivalent) before the tool call.
  4. Constrain/prompt the LLM or use structured output so emitted tool arguments are valid JSON.

Example fix

// before
callTool("t", "{name:'x'}");
// after
callTool("t", new Gson().toJson(Map.of("name", "x"))); // {"name":"x"}
Defensive patterns

Strategy: validation

Validate before calling

boolean isValidJsonObject(String s) {
    try {
        JsonObject o = com.google.gson.JsonParser.parseString(s).getAsJsonObject();
        return o != null;
    } catch (Exception e) { return false; }
}

Try / catch

try {
    return callback.call(args, ctx);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Invalid JSON format")) {
        log.warn("Tool arguments not valid JSON: {}", e.getMessage());
        return "Please provide arguments as a valid JSON object";
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling the tool with an arguments string that Gson cannot parse, e.g. "{name: }", trailing commas, single quotes, or non-JSON text emitted by the LLM.

Common situations: LLM-generated tool arguments that are malformed JSON; clients doing naive string concatenation instead of JSON serialization; encoding issues truncating the payload.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/bbf2548fb1c2ab25. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java:348

     * Parses the input JSON string into a JsonObject.
     *
     * @param input the input JSON string
     * @return the parsed JsonObject
     * @throws IllegalArgumentException if input is not valid JSON
     */
    private JsonObject parseInput(final String input) {
        try {
            if (org.apache.commons.lang3.StringUtils.isBlank(input)) {
                return new JsonObject();
            }
            final JsonObject inputJson = GsonUtils.getInstance().fromJson(input, JsonObject.class);
            if (Objects.isNull(inputJson)) {
                throw new IllegalArgumentException("Invalid input JSON format");
            }
            return inputJson;
        } catch (Exception e) {
            LOG.error("Failed to parse input JSON: {}", e.getMessage());
            throw new IllegalArgumentException("Invalid JSON format: " + e.getMessage(), e);
        }
    }

    /**
     * Builds request configuration from configuration string and input parameters.
     *
     * @param configStr the configuration string
     * @param inputJson the input parameters
     * @return the built request configuration
     */
    private RequestConfig buildRequestConfig(final String configStr, final JsonObject inputJson) {
        final RequestConfigHelper configHelper = new RequestConfigHelper(configStr);
        final JsonObject requestTemplate = configHelper.getRequestTemplate();
        final JsonObject argsPosition = configHelper.getArgsPosition();
        final String urlTemplate = configHelper.getUrlTemplate();
        final String method = configHelper.getMethod();
        final boolean argsToJsonBody = configHelper.isArgsToJsonBody();

View on GitHub (pinned to 567142e072)