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

  1. Pre-filter tool messages in your own code before calling saveAll so the drop is intentional and warning-free
  2. 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)
  3. Store tool interactions in a separate application table and merge them back when loading conversation history
  4. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/ab08e916e9abb00f. Report an issue: GitHub.