apache/shenyu · error · RuntimeException
Tool execution failed
Error message
Tool execution failed: ${e.getMessage()} What it means
ShenyuToolCallback.call wraps the entire MCP tool invocation (session lookup, input parsing, config extraction, downstream HTTP execution) in a try/catch. Any exception escaping the inner execution is logged and rethrown as a RuntimeException with the prefix 'Tool execution failed:'. It is a generic barrier so MCP clients get a message instead of a raw framework stack.
Solutions
- Read the 'cause' of the RuntimeException and the LOG.error output — the real failure is the wrapped exception.
- Check the log line 'Failed to process tool call for <tool>' for tool name and root cause.
- Validate that the tool's requestConfig JSON in the admin MCP config is non-empty and well-formed.
- Verify the MCP client is sending valid JSON arguments matching the tool schema.
- Confirm the target backend service is reachable from the gateway.
Defensive patterns
Strategy: try-catch
Validate before calling
if (toolDefinition == null || !(toolDefinition instanceof ShenyuToolDefinition sdef)
|| !StringUtils.hasText(sdef.requestConfig())) {
throw new IllegalArgumentException("Tool definition/config invalid before call");
} Type guard
boolean isShenyuTool(ToolDefinition d) {
return d instanceof ShenyuToolDefinition;
} Try / catch
try {
return callback.call(args, toolContext);
} catch (RuntimeException e) {
Throwable root = e.getCause() != null ? e.getCause() : e;
log.error("MCP tool '{}' failed: {}", toolName, root.getMessage(), root);
return "Tool temporarily unavailable, please retry later";
} Prevention
- Always read e.getCause() — the outer message is only a wrapper.
- Validate tool definitions (type + requestConfig) at registration time, not call time.
- Keep gateway-to-backend health checks so downstream failures surface early.
- Enable LOG.error output review for 'Failed to process tool call'.
When it happens
Trigger: Any exception during call(): invalid ShenyuToolDefinition, empty request config, malformed input JSON, tool execution timeout, or downstream request failure inside executeToolCall.
Common situations: MCP client sends malformed JSON arguments; downstream service unreachable; session exchange missing from the holder; tool config in admin dashboard missing requestConfig.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Tool definition must be of type ShenyuToolDefinition, got
- Request configuration cannot be empty
- Tool execution timeout or error
- Invalid input JSON format
- Invalid JSON format
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/c5af4c37ece45341.
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:160
// Extract MCP session context (must be pre-established)
final McpSyncServerExchange mcpExchange = extractMcpExchange(toolContext);
final String sessionId = extractSessionId(mcpExchange);
// Validate and extract tool configuration
final ShenyuToolDefinition shenyuTool = validateToolDefinition();
final String configStr = extractRequestConfig(shenyuTool);
// Get pre-stored exchange and plugin chain
final ServerWebExchange originExchange = getOriginExchange(sessionId);
final ShenyuPluginChain chain = getPluginChain(originExchange);
// Execute the tool call through the plugin chain
return executeToolCall(originExchange, chain, sessionId, configStr, input);
} catch (Exception e) {
LOG.error("Failed to process tool call for '{}': {}", toolDefinition.name(), e.getMessage(), e);
throw new RuntimeException("Tool execution failed: " + e.getMessage(), e);
}
}
/**
* Validates and casts the tool definition to Shenyu-specific type.
*
* @return the Shenyu tool definition
* @throws IllegalStateException if tool definition is not of expected type
*/
private ShenyuToolDefinition validateToolDefinition() {
if (!(this.toolDefinition instanceof ShenyuToolDefinition)) {
throw new IllegalStateException("Tool definition must be of type ShenyuToolDefinition, got: "
+ this.toolDefinition.getClass().getSimpleName());
}
return (ShenyuToolDefinition) this.toolDefinition;
}
/**View on GitHub (pinned to 567142e072)