{"record":{"id":"fa2613510e5ee783","repo":"YunaiV/yudao-cloud","slug":"error-fa2613","errorCode":null,"errorMessage":"未知消息类型({})","messagePattern":"未知消息类型\\((.+?)\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":500,"severity":"error","filePath":"yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java","lineNumber":150,"sourceCode":"            default:\n                throw new IllegalArgumentException(StrUtil.format(\"未知平台({})\", platform));\n        }\n    }\n\n    public static Message buildMessage(String type, String content) {\n        if (MessageType.USER.getValue().equals(type)) {\n            return new UserMessage(content);\n        }\n        if (MessageType.ASSISTANT.getValue().equals(type)) {\n            return new AssistantMessage(content);\n        }\n        if (MessageType.SYSTEM.getValue().equals(type)) {\n            return new SystemMessage(content);\n        }\n        if (MessageType.TOOL.getValue().equals(type)) {\n            throw new UnsupportedOperationException(\"暂不支持 tool 消息：\" + content);\n        }\n        throw new IllegalArgumentException(StrUtil.format(\"未知消息类型({})\", type));\n    }\n\n    public static Map<String, Object> buildCommonToolContext() {\n        Map<String, Object> context = new HashMap<>();\n        context.put(TOOL_CONTEXT_LOGIN_USER, SecurityFrameworkUtils.getLoginUser());\n        context.put(TOOL_CONTEXT_TENANT_ID, TenantContextHolder.getTenantId());\n        return context;\n    }\n\n    @SuppressWarnings(\"ConstantValue\")\n    public static String getChatResponseContent(ChatResponse response) {\n        if (response == null\n                || response.getResult() == null\n                || response.getResult().getOutput() == null) {\n            return null;\n        }\n        return response.getResult().getOutput().getText();\n    }","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-module-ai/yudao-module-ai-server/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java#L132-L168","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","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"],"exampleFix":"// before\nMessage msg = AiUtils.buildMessage(rawType, content); // throws on \"User\"/null\n\n// after\nMessageType t = Arrays.stream(MessageType.values())\n    .filter(e -> e.getValue().equalsIgnoreCase(StrUtil.trimToEmpty(rawType)))\n    .findFirst()\n    .orElseThrow(() -> new IllegalArgumentException(\"未知消息类型(\" + rawType + \")\"));\nMessage msg = AiUtils.buildMessage(t.getValue(), content);","handlingStrategy":"type-guard","validationCode":"Set<String> OK = Set.of(\"user\", \"assistant\", \"system\", \"tool\");\nif (rawType == null || !OK.contains(rawType.toLowerCase())) {\n    throw new IllegalArgumentException(\"消息类型必须为 \" + OK + \", 实际: \" + rawType);\n}","typeGuard":"boolean isKnownMessageType(String type) {\n    return Arrays.stream(MessageType.values())\n        .anyMatch(e -> e.getValue().equalsIgnoreCase(StrUtil.trimToEmpty(type)));\n}","tryCatchPattern":"try {\n    return AiUtils.buildMessage(type, content);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"未知消息类型\")) { /* map to a 400 for the caller */ }\n    throw e;\n}","preventionTips":["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"],"tags":["ai","message-type","enum","validation"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}