redis/jedis · error · JedisException
Could not enable client tracking. Reply
Error message
Could not enable client tracking. Reply: ${reply} What it means
After registering the invalidation push consumer, initializeClientSideCache sends CLIENT TRACKING ON and expects a simple status reply of OK. Any other reply (e.g. ERR from a server that rejects or does not support tracking, or a proxy swallowing it) raises this JedisException containing the raw reply. This catches servers that passed the version check but still refused the command.
Solutions
- Read the reply text in the exception message and fix the server-side cause (e.g. un-rename/enable the CLIENT command).
- Ensure the client connects directly to a Redis 7.4+ node, not through a proxy that strips CLIENT subcommands.
- Check server logs for why CLIENT TRACKING was rejected (maxclients, unsupported mode, wrong protocol).
- Retry connection establishment if the failure was transient (e.g. failover raced during init).
Example fix
// server side (redis.conf) // before: rename-command CLIENT "" // after: remove the rename so CLIENT TRACKING works # rename-command CLIENT "" <-- delete this line
Defensive patterns
Strategy: try-catch
Validate before calling
// verify CLIENT TRACKING manually before enabling csc String reply = jedis.sendCommand(Protocol.Command.CLIENT, "TRACKING", "OFF").getStatusCodeReply(); // ensure reply == OK; ERR indicates tracking is blocked on this server/proxy
Try / catch
try {
CacheConnection conn = CacheConnection.builder(cache).clientConfig(cfg).build();
} catch (JedisException e) {
if (e.getMessage().startsWith("Could not enable client tracking")) {
// fall back to a non-caching connection or alert ops (proxy/renamed CLIENT command)
} else throw e;
} Prevention
- Do not rename or disable the CLIENT command on servers used for client-side caching
- Avoid proxies (Twemproxy/Codis) that strip CLIENT subcommands in front of caching connections
- Verify tracking support with CLIENT TRACKING OFF / INFO during infrastructure smoke tests
When it happens
Trigger: CLIENT TRACKING ON returns a non-OK status reply — e.g. server rejects tracking due to maxmemory-policy/proxy interference, command renamed or disabled via rename-command, a proxy in front of Redis not forwarding CLIENT subcommands, or RESP3 push negotiation failing.
Common situations: Deployments behind HAProxy/Twemproxy that filter CLIENT commands; secured clusters where CLIENT is renamed; Redis Enterprise or managed services restricting CLIENT TRACKING; race conditions where the connection is switched to another server mid-init.
Related errors
- Client-side caching is only supported with RESP3.
- Client-side caching is only supported with RESP3.
- is not supported. Value: " ".
- Cache cannot be null!
- Cache is required to build CacheConnection.
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/1984a4acd230adc8.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/csc/CacheConnection.java:164
private void initializeClientSideCache() {
if (getRedisProtocol() != RedisProtocol.RESP3) {
throw new JedisException("Client side caching is only supported with RESP3.");
}
Objects.requireNonNull(cache);
if (!cache.compatibilityMode()) {
RedisVersion current = new RedisVersion(version);
RedisVersion required = new RedisVersion(MIN_REDIS_VERSION);
if (!REDIS.equals(server) || current.compareTo(required) < 0) {
throw new JedisException(
String.format("Client side caching is only supported with 'Redis %s' or later.", MIN_REDIS_VERSION));
}
}
addPushConsumer(new PushInvalidateConsumer(cache));
sendCommand(Protocol.Command.CLIENT, "TRACKING", "ON");
String reply = getStatusCodeReply();
if (!"OK".equals(reply)) {
throw new JedisException("Could not enable client tracking. Reply: " + reply);
}
}
@SuppressWarnings("rawtypes")
private CacheEntry validateEntry(CacheEntry cacheEntry) {
CacheConnection cacheOwner = cacheEntry.getConnection();
if (cacheOwner == null || cacheOwner.isBroken() || !cacheOwner.isConnected()) {
cache.delete(cacheEntry.getCacheKey());
return null;
} else {
try {
cacheOwner.readPushesWithCheckingBroken();
} catch (JedisException e) {
cache.delete(cacheEntry.getCacheKey());
return null;
}
return cache.get(cacheEntry.getCacheKey());View on GitHub (pinned to 6dac31d4c2)