{"record":{"id":"12ee1ebe45263c3d","repo":"spring-projects/spring-ai","slug":"cassandrachatmemoryrepository-does-not-support-too","errorCode":null,"errorMessage":"CassandraChatMemoryRepository does not support tool call messages. Some messages were filtered out for conversation: + conversationId","messagePattern":"CassandraChatMemoryRepository does not support tool call messages\\. Some messages were filtered out for conversation: \\+ conversationId","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"memory-repositories/spring-ai-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepository.java","lineNumber":141,"sourceCode":"\t\t\t\t\tmessages.add(msg);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn messages;\n\t}\n\n\t@Override\n\tpublic void saveAll(String conversationId, List<Message> messages) {\n\t\tAssert.hasText(conversationId, \"conversationId cannot be null or empty\");\n\t\tAssert.notNull(messages, \"messages cannot be null\");\n\t\tAssert.noNullElements(messages, \"messages cannot contain null elements\");\n\n\t\tList<Message> persistableMessages = messages.stream()\n\t\t\t.filter(m -> !(m instanceof ToolResponseMessage)\n\t\t\t\t\t&& !(m instanceof AssistantMessage am && am.hasToolCalls()))\n\t\t\t.toList();\n\t\tif (logger.isWarnEnabled() && persistableMessages.size() < messages.size()) {\n\t\t\tlogger.warn(\n\t\t\t\t\t\"CassandraChatMemoryRepository does not support tool call messages. Some messages were filtered out for conversation: \"\n\t\t\t\t\t\t\t+ conversationId);\n\t\t}\n\n\t\tInstant instant = Instant.now();\n\t\tList<Object> primaryKeys = this.conf.primaryKeyTranslator.apply(conversationId);\n\t\tBoundStatementBuilder builder = this.addStmt.boundStatementBuilder();\n\n\t\tfor (int k = 0; k < primaryKeys.size(); ++k) {\n\t\t\tCassandraChatMemoryRepositoryConfig.SchemaColumn keyColumn = this.conf.getPrimaryKeyColumn(k);\n\t\t\tbuilder = builder.set(keyColumn.name(), primaryKeys.get(k), keyColumn.javaType());\n\t\t}\n\n\t\tList<UdtValue> msgs = new ArrayList<>();\n\t\tfor (Message msg : persistableMessages) {\n\n\t\t\tPreconditions.checkArgument(\n\t\t\t\t\t!msg.getMetadata().containsKey(CONVERSATION_TS)","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/memory-repositories/spring-ai-model-chat-memory-repository-cassandra/src/main/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepository.java#L123-L159","documentation":"CassandraChatMemoryRepository.saveAll() filters out ToolResponseMessage and AssistantMessage instances that carry tool calls before persisting, because the Cassandra schema cannot store them. When any messages were dropped, it logs this warning naming the conversationId. Tool-call context is silently not persisted to Cassandra, so restored conversations lose tool history.","triggerScenarios":"Calling saveAll (directly or via addAndGet) with a message list that contains a ToolResponseMessage or an AssistantMessage with hasToolCalls() == true for a given conversationId.","commonSituations":"Using tool-calling chat models with ChatMemory backed by Cassandra; agents that record tool executions in memory; replaying or migrating conversations that include tool-call history into Cassandra.","solutions":["Strip tool-call messages yourself before saving so you control what is lost (filter ToolResponseMessage and AssistantMessage with tool calls)","Switch to a repository that supports tool-call persistence (e.g. JDBC/JDBC-compatible stores or the in-memory repository) if tool history matters","Persist tool-call context out-of-band (your own table keyed by conversationId) and reattach it when reloading the conversation","Suppress the noise by filtering before the repository call so sizes match and the warning never fires"],"exampleFix":"// before\nchatMemory.add(conversationId, assistantMessageWithToolCalls);\n\n// after: persist only plain text messages\nif (!(msg instanceof ToolResponseMessage)\n        && !(msg instanceof AssistantMessage am && am.hasToolCalls())) {\n    chatMemory.add(conversationId, msg);\n}","handlingStrategy":"validation","validationCode":"boolean persistable(Message m) {\n    return !(m instanceof ToolResponseMessage)\n        && !(m instanceof AssistantMessage am && am.hasToolCalls());\n}\n// call only: messages.stream().filter(this::persistable)... for Cassandra-backed memory","typeGuard":"static boolean isToolCallMessage(Message m) {\n    return m instanceof ToolResponseMessage\n        || (m instanceof AssistantMessage am && am.hasToolCalls());\n}","tryCatchPattern":null,"preventionTips":["Remember Cassandra-backed memory drops tool messages — plan tool history separately","Filter before calling addAndGet/saveAll so losses are intentional","Write an integration test asserting tool messages are absent after reload","Pin supported message types in a small wrapper around the repository"],"tags":["cassandra","chat-memory","tool-calls","message-filtering"],"backgroundTag":"unsupported-operation","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"}