spring-projects/spring-ai · warning

Error reserving atomic timestamps for conversation + convers

Error message

Error reserving atomic timestamps for conversation + conversationId + , using fallback: + e.getMessage()

What it means

RedisChatMemoryRepository.reserveTimestampsForConversation() tries to atomically reserve timestamps in Redis for a conversation; if the Redis operation throws any Exception it logs this warning and falls back to a locally computed timestamp (Instant.now().toEpochMilli()*1000 + nanoTime%1000). Functionality continues but timestamp ordering/uniqueness guarantees across nodes are weakened.

Source

Thrown at memory-repositories/spring-ai-model-chat-memory-repository-redis/src/main/java/org/springframework/ai/chat/memory/repository/redis/RedisChatMemoryRepository.java:230

			long nextTimestamp = Long.parseLong(result.toString());

			// Set expiration on the counter key (same as the messages)
			if (this.config.getTimeToLiveSeconds() != -1) {
				this.jedisClient.expire(sequenceKey, this.config.getTimeToLiveSeconds());
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Reserved " + count + " timestamp(s) starting at " + nextTimestamp + " for conversation "
						+ conversationId);
			}

			return nextTimestamp;
		}

		catch (Exception e) {
			// Log error and fall back to current timestamp with nanoTime for uniqueness
			if (logger.isWarnEnabled()) {
				logger.warn("Error reserving atomic timestamps for conversation " + conversationId
						+ ", using fallback: " + e.getMessage());
			}
			// Add nanoseconds to ensure uniqueness even in fallback scenario
			return Instant.now().toEpochMilli() * 1000 + (System.nanoTime() % 1000);
		}
	}

	public List<Message> get(String conversationId) {
		return get(conversationId, this.config.getMaxMessagesPerConversation());
	}

	public List<Message> get(String conversationId, int lastN) {
		Assert.notNull(conversationId, "Conversation ID must not be null");
		Assert.isTrue(lastN > 0, "LastN must be greater than 0");

		// Use QueryBuilders to create a tag field query for conversation_id
		QueryNode queryNode = QueryBuilders.intersect("conversation_id",
				Values.tags(RediSearchUtil.escape(conversationId)));

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Check Redis availability and the underlying connection exception in logs (only e.getMessage() is logged)
  2. Verify spring.data.redis host/port/password and that Redis accepts commands (redis-cli ping)
  3. Fix pool/timeout settings if errors are caused by pool exhaustion or timeouts
  4. After connectivity is restored, re-write affected messages if strict ordering matters — fallback timestamps may not be globally unique
  5. Consider enabling Redis persistence/sentinel so the atomic reserve path stays healthy

Example fix

// before
Long ts = repository.timestamp(conversationId); // silently falls back on Redis failure
// after
if (!redisHealthy()) {
    throw new IllegalStateException("Redis unavailable; timestamp reservation would fall back");
}
Long ts = repository.timestamp(conversationId);
Defensive patterns

Strategy: fallback

Validate before calling

try (Jedis j = pool.getResource()) { assertEquals("PONG", j.ping()); } // check Redis health before writes

Type guard

boolean redisHealthy = connection != null && "PONG".equals(connection.ping());

Try / catch

try { long ts = repository.timestamp(conversationId); }
catch (Exception e) { log.warn("timestamp reservation failed; ordering not guaranteed across instances", e); throw e; } // or apply your own monotonic per-instance sequence

Prevention

When it happens

Trigger: Redis connection failure, timeouts, or errors from the Lua/atomic reserve command inside reserveTimestampsForConversation, invoked via nextTimestamp() and timestamp() while writing messages.

Common situations: Redis down or restarted mid-run; network blips between app and Redis; Redis cluster failover; connection pool exhaustion; wrong host/port causing connection errors on first write.

Related errors


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