spring-projects/spring-ai · warning
JdbcChatMemoryRepository does not support tool call messages
Error message
JdbcChatMemoryRepository does not support tool call messages. Some messages were filtered out for conversation: + conversationId
What it means
JdbcChatMemoryRepository.saveAll() filters out ToolResponseMessage and tool-calling AssistantMessage rows because the JDBC schema has no representation for them, logging this warning when messages were dropped. Tool-call context is not persisted, so reloaded conversations lose tool history.
Source
Thrown at memory-repositories/spring-ai-model-chat-memory-repository-jdbc/src/main/java/org/springframework/ai/chat/memory/repository/jdbc/JdbcChatMemoryRepository.java:117
Assert.hasText(conversationId, "conversationId cannot be null or empty");
return this.jdbcTemplate.query(this.dialect.getSelectMessagesSql(), new MessageRowMapper(), conversationId)
.stream()
.filter(Objects::nonNull)
.toList();
}
@Override
public void saveAll(String conversationId, List<Message> messages) {
Assert.hasText(conversationId, "conversationId cannot be null or empty");
Assert.notNull(messages, "messages cannot be null");
Assert.noNullElements(messages, "messages cannot contain null elements");
List<Message> persistableMessages = messages.stream()
.filter(m -> !(m instanceof ToolResponseMessage)
&& !(m instanceof AssistantMessage am && am.hasToolCalls()))
.toList();
if (logger.isWarnEnabled() && persistableMessages.size() < messages.size()) {
logger.warn(
"JdbcChatMemoryRepository does not support tool call messages. Some messages were filtered out for conversation: "
+ conversationId);
}
this.transactionTemplate.executeWithoutResult(status -> {
deleteByConversationId(conversationId);
this.jdbcTemplate.batchUpdate(this.dialect.getInsertMessageSql(),
new AddBatchPreparedStatement(conversationId, persistableMessages));
});
}
@Override
public void deleteByConversationId(String conversationId) {
Assert.hasText(conversationId, "conversationId cannot be null or empty");
this.jdbcTemplate.update(this.dialect.getDeleteMessagesSql(), conversationId);
}
public static Builder builder() {View on GitHub (pinned to 98a7beda4f)
Solutions
- Pre-filter tool messages in your own code before calling saveAll so the drop is intentional and warning-free
- If tool history is required, use a store/abstraction that persists tool-call messages (e.g. keep in-memory repository, or upgrade to a version adding tool support)
- Store tool interactions in a separate application table and merge them back when loading conversation history
- Check for a newer spring-ai version — tool-call memory support has been expanding across repositories
Example fix
// before
memory.add(conversationId, toolResponseMessage); // silently filtered, warning logged
// after
if (msg instanceof ToolResponseMessage || (msg instanceof AssistantMessage am && am.hasToolCalls())) {
toolHistoryStore.save(conversationId, msg); // your own persistence
} else {
memory.add(conversationId, msg);
} Defensive patterns
Strategy: validation
Validate before calling
boolean persistable(Message m) {
return !(m instanceof ToolResponseMessage)
&& !(m instanceof AssistantMessage am && am.hasToolCalls());
} Type guard
static boolean dropsToolHistory(Message m) {
return m instanceof ToolResponseMessage
|| (m instanceof AssistantMessage am && am.hasToolCalls());
} Prevention
- Pre-filter tool messages before saveAll/addAndGet with JDBC memory
- Test conversation round-trips (save + reload) in CI with tool-calling flows
- If tool history matters, choose a store that persists it or persist separately
- Watch the warn log as a signal your memory backend loses tool context
When it happens
Trigger: Calling saveAll (directly or via addAndGet) with a message list containing ToolResponseMessage or AssistantMessage with tool calls for the given conversationId; saveAll then deletes and rewrites the conversation rows without them.
Common situations: Tool-calling agents (OpenAI function calling, etc.) using JDBC-backed ChatMemory; switching from an in-memory repository (which kept tool messages) to JDBC and noticing lost tool history; long agent conversations that exceed context after reload.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- CassandraChatMemoryRepository does not support tool call mes
- MongoChatMemoryRepository does not support tool call message
- Explicitly set dialect + explicitDialect.getClass().getSimpl
- unknown message type %s
- DataSource must be set (either via dataSource() or jdbcTempl
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/ab08e916e9abb00f.
Report an issue: GitHub.