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
- Read the wrapped cause message to see the exact Gson parse error and position.
- Fix the client to serialize arguments with a proper JSON library instead of string building.
- Validate the arguments string with JSON.parse (or equivalent) before the tool call.
- 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
- Pre-validate arguments with JSON.parse / Gson before the call.
- Use structured/JSON-mode output from the LLM to avoid malformed tool arguments.
- Log the raw arguments string alongside the parse error for debugging.
- Encode payloads consistently (UTF-8) to avoid truncation artifacts.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid input JSON format
- Invalid request body: expected a JSON object
- OpenAPI document is missing the top-level 'servers' field…
- Invalid Swagger JSON format:
- OpenAPI pathKey cannot be null or empty
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)