{"record":{"id":"380fbaecb90c2b2c","repo":"spring-projects/spring-ai","slug":"unknown-message-type-type","errorCode":null,"errorMessage":"Unknown message type: + type","messagePattern":"Unknown message type: \\+ type","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"memory-repositories/spring-ai-model-chat-memory-repository-redis/src/main/java/org/springframework/ai/chat/memory/repository/redis/RedisChatMemoryRepository.java","lineNumber":355,"sourceCode":"\t\t\t\t\t\tJsonArray responseArray = json.getAsJsonArray(\"toolResponses\");\n\t\t\t\t\t\tfor (JsonElement responseElement : responseArray) {\n\t\t\t\t\t\t\tJsonObject responseJson = responseElement.getAsJsonObject();\n\n\t\t\t\t\t\t\tString id = responseJson.has(\"id\") ? responseJson.get(\"id\").getAsString() : \"\";\n\t\t\t\t\t\t\tString name = responseJson.has(\"name\") ? responseJson.get(\"name\").getAsString() : \"\";\n\t\t\t\t\t\t\tString responseData = responseJson.has(\"responseData\")\n\t\t\t\t\t\t\t\t\t? responseJson.get(\"responseData\").getAsString() : \"\";\n\n\t\t\t\t\t\t\ttoolResponses.add(new ToolResponseMessage.ToolResponse(id, name, responseData));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tmessages.add(ToolResponseMessage.builder().responses(toolResponses).metadata(metadata).build());\n\t\t\t\t}\n\t\t\t\t// Add handling for other message types if needed\n\t\t\t\telse {\n\t\t\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\t\t\tlogger.warn(\"Unknown message type: \" + type);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (logger.isDebugEnabled()) {\n\t\t\tlogger.debug(\"Returning \" + messages.size() + \" messages for conversation \" + conversationId);\n\t\t\tmessages.forEach(message -> logger.debug(\"Message type: \" + message.getMessageType() + \", content: \"\n\t\t\t\t\t+ message.getText() + \", class: \" + message.getClass().getSimpleName()));\n\t\t}\n\n\t\treturn messages;\n\t}\n\n\tpublic void clear(String conversationId) {\n\t\tAssert.notNull(conversationId, \"Conversation ID must not be null\");\n\n\t\t// Use QueryBuilders to create a tag field query","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/memory-repositories/spring-ai-model-chat-memory-repository-redis/src/main/java/org/springframework/ai/chat/memory/repository/redis/RedisChatMemoryRepository.java#L337-L373","documentation":"While deserializing stored messages in RedisChatMemoryRepository.get(), the code switches on a persisted 'type' field to rebuild AssistantMessage/SystemMessage/UserMessage/ToolResponseMessage. If the stored type string matches none of the known kinds it logs this warning and simply skips the message — it is not added to the returned list, so messages can silently disappear.","triggerScenarios":"Reading a conversation whose stored message entries contain a 'type' value outside {user, assistant, system, tool} — e.g. data written by an older/newer library version, hand-seeded Redis data, or corrupted/mistyped JSON.","commonSituations":"Spring AI version upgrade or downgrade changing the Message type taxonomy; data written by a custom serializer; manual edits to Redis JSON; messages written by another application sharing the same keyspace.","solutions":["Inspect the offending Redis entries (keys under the conversation) and check their 'type' field values","Align writer and reader to the same Spring AI version so type tags match","Migrate or delete legacy entries with unknown types (e.g. rewrite them as user/assistant messages)","If you intentionally store custom types, extend/deserialize them before calling the repository","Back up and clear the affected conversationId key if the data is stale"],"exampleFix":"// before\n// stored: {\"type\":\"function_call\", ...}  -> message silently dropped\n// after\n// migrate in Redis:\n// HSET/JSON.SET ... type -> \"tool\" (or delete the legacy entry)\n// then repository.get(conversationId) returns all messages","handlingStrategy":"validation","validationCode":"Set<String> known = Set.of(\"user\",\"assistant\",\"system\",\"tool\");\nfor (Object o : storedMessages) {\n    String t = extractType(o);\n    if (!known.contains(t)) throw new IllegalStateException(\"Unknown stored message type: \" + t);\n}","typeGuard":"static boolean isKnownMessageType(String type) {\n    return type != null && Set.of(\"user\",\"assistant\",\"system\",\"tool\").contains(type.toLowerCase());\n}","tryCatchPattern":"try { List<Message> msgs = repository.get(conversationId); }\ncatch (Exception e) { /* log */ }\n// plus reconcile count: if msgs.size() < storedCount, some entries had unknown types","preventionTips":["Pin one Spring AI version across writer and reader services","Never hand-edit Redis chat-memory JSON; use the repository API","Validate any external writers sharing the keyspace","Alert on this warning — dropped messages are silent data loss"],"tags":["redis","deserialization","message-type","chat-memory"],"backgroundTag":"unexpected-response-shape","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}