YunaiV/yudao-cloud · error · IllegalArgumentException
未知消息类型({})
Error message
未知消息类型({}) What it means
AiUtils.buildMessage throws IllegalArgumentException when the message type string matches none of user/assistant/system/tool. It is a strict whitelist: any other value (including null, empty, or a typo like 'USER ' with whitespace or localized values) falls through to the format error. The type almost always comes from the ai_message/knowledge message 'type' column or a request VO field.
Source
Thrown at yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java:150
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;
}
return response.getResult().getOutput().getText();
}View on GitHub (pinned to 477be9dd49)
Solutions
- Normalize the type to MessageType enum values ('user', 'assistant', 'system', 'tool') before calling buildMessage
- Check the DB rows / request payloads for null or unexpected type values and repair them
- Align the frontend enum with the server MessageType constants
Example fix
// before
Message msg = AiUtils.buildMessage(rawType, content); // throws on "User"/null
// after
MessageType t = Arrays.stream(MessageType.values())
.filter(e -> e.getValue().equalsIgnoreCase(StrUtil.trimToEmpty(rawType)))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("未知消息类型(" + rawType + ")"));
Message msg = AiUtils.buildMessage(t.getValue(), content); Defensive patterns
Strategy: type-guard
Validate before calling
Set<String> OK = Set.of("user", "assistant", "system", "tool");
if (rawType == null || !OK.contains(rawType.toLowerCase())) {
throw new IllegalArgumentException("消息类型必须为 " + OK + ", 实际: " + rawType);
} Type guard
boolean isKnownMessageType(String type) {
return Arrays.stream(MessageType.values())
.anyMatch(e -> e.getValue().equalsIgnoreCase(StrUtil.trimToEmpty(type)));
} Try / catch
try {
return AiUtils.buildMessage(type, content);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("未知消息类型")) { /* map to a 400 for the caller */ }
throw e;
} Prevention
- Validate the type field at the API boundary before persisting or building
- Share one enum source between frontend and backend (or generate the TS enum from Java)
- Add a DB check constraint on the message type column
When it happens
Trigger: Calling buildMessage with type values like null, "", "tool_call", "function", or wrong-case strings; DB rows with a type written by an older version or by hand; a request payload where the frontend sends its own enum spelling instead of the server's lowercase values.
Common situations: Data migration importing messages with types the enum does not define; frontend/backend enum drift after upgrading yudao AI module; manually inserted test rows with uppercase 'User'.
Related errors
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/fa2613510e5ee783.
Report an issue: GitHub.