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
- Check Redis availability and the underlying connection exception in logs (only e.getMessage() is logged)
- Verify spring.data.redis host/port/password and that Redis accepts commands (redis-cli ping)
- Fix pool/timeout settings if errors are caused by pool exhaustion or timeouts
- After connectivity is restored, re-write affected messages if strict ordering matters — fallback timestamps may not be globally unique
- 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
- Monitor Redis availability and connection-pool saturation
- Configure redis timeouts/retries so transient blips don't hit the atomic reserve path
- Remember the fallback is per-JVM; avoid multi-node ordering assumptions when warnings appear
- Keep Redis persisted and use Sentinel/Cluster for failover
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
- Unknown message type: + type
- Unknown message type: + type + , returning generic UserMessa
- Could not parse text search score: ${doc.getString("$score")
- unknown message type %s
- Unsupported message type:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/ba85d03db4c42b1a.
Report an issue: GitHub.