YunaiV/yudao-cloud · error · UnsupportedOperationException
暂不支持 tool 消息:{}
Error message
暂不支持 tool 消息:{} What it means
AiUtils.buildMessage can construct UserMessage, AssistantMessage and SystemMessage for Spring AI, but throws UnsupportedOperationException for knowledge/role messages whose type is 'tool'. Tool messages carry structured call results (name, arguments, response) that cannot be faithfully rebuilt from a plain string, so the builder refuses them by design.
Source
Thrown at yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java:148
return OllamaChatOptions.builder().model(model).temperature(temperature).numPredict(maxTokens)
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
default:
throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
}
}
public static Message buildMessage(String type, String content) {
if (MessageType.USER.getValue().equals(type)) {
return new UserMessage(content);
}
if (MessageType.ASSISTANT.getValue().equals(type)) {
return new AssistantMessage(content);
}
if (MessageType.SYSTEM.getValue().equals(type)) {
return new SystemMessage(content);
}
if (MessageType.TOOL.getValue().equals(type)) {
throw new UnsupportedOperationException("暂不支持 tool 消息:" + content);
}
throw new IllegalArgumentException(StrUtil.format("未知消息类型({})", type));
}
public static Map<String, Object> buildCommonToolContext() {
Map<String, Object> context = new HashMap<>();
context.put(TOOL_CONTEXT_LOGIN_USER, SecurityFrameworkUtils.getLoginUser());
context.put(TOOL_CONTEXT_TENANT_ID, TenantContextHolder.getTenantId());
return context;
}
@SuppressWarnings("ConstantValue")
public static String getChatResponseContent(ChatResponse response) {
if (response == null
|| response.getResult() == null
|| response.getResult().getOutput() == null) {
return null;
}View on GitHub (pinned to 477be9dd49)
Solutions
- Filter out TOOL-type messages before calling buildMessage (they are not renderable prompt content)
- If tool context is genuinely needed, construct a Spring AI ToolResponseMessage explicitly instead of via this generic builder
- Fix the upstream data/UI so tool-interaction records are not sent as ordinary chat messages
Example fix
// before
List<Message> messages = messages.stream()
.map(m -> AiUtils.buildMessage(m.getType(), m.getContent()))
.collect(Collectors.toList());
// after
List<Message> messages = messages.stream()
.filter(m -> !MessageType.TOOL.getValue().equals(m.getType())) // drop tool rows
.map(m -> AiUtils.buildMessage(m.getType(), m.getContent()))
.collect(Collectors.toList()); Defensive patterns
Strategy: validation
Validate before calling
// drop non-renderable message types before building prompts
List<Message> messages = rows.stream()
.filter(r -> !MessageType.TOOL.getValue().equals(r.getType()))
.filter(r -> isRenderable(r.getType()))
.map(r -> AiUtils.buildMessage(r.getType(), r.getContent()))
.toList(); Type guard
boolean isRenderable(String type) {
return MessageType.USER.getValue().equals(type)
|| MessageType.ASSISTANT.getValue().equals(type)
|| MessageType.SYSTEM.getValue().equals(type);
} Try / catch
try {
messages.add(AiUtils.buildMessage(type, content));
} catch (UnsupportedOperationException e) { // tool rows are not prompt content
log.debug("skipping non-renderable message type={}", type);
} Prevention
- Filter persisted history to user/assistant/system before prompt assembly
- Represent tool interactions in a dedicated table/field, not as generic messages
- Reject type=tool at the request VO level in your controller
When it happens
Trigger: Calling buildMessage with a persisted ai knowledge message row whose type column equals the TOOL enum value; re-hydrating chat history that contains tool-call results and passing it through this helper; a client sending type=tool in a message list to an AI chat/conversation endpoint that funnels into buildMessage.
Common situations: Conversation history containing tool interactions being replayed into a new prompt; admin UI letting users paste arbitrary message types into a role prompt; migrating data where message type was mis-tagged as tool.
Related errors
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/1a66aca4b30a7be3.
Report an issue: GitHub.