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

  1. Filter out TOOL-type messages before calling buildMessage (they are not renderable prompt content)
  2. If tool context is genuinely needed, construct a Spring AI ToolResponseMessage explicitly instead of via this generic builder
  3. 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

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.