redis/redis-py · error · ValueError

Cannot create cache key.

Error message

Cannot create cache key.

What it means

Raised in CacheProxyConnection.send_command (connection.py:1779-1780) when the command is deemed cacheable by the cache (is_cachable returned True) but the caller did not pass keys=... . To build a CacheKey the proxy needs the keys the command operates on; if they are absent it cannot identify the cache entry, so it raises ValueError. This is an internal/library-side contract: cacheable commands must be dispatched with their keys.

Solutions

  1. Ensure cacheable read commands are sent through the standard client API that supplies keys (e.g. r.get('k') not raw conn.send_command('GET')).
  2. If using send_command directly with caching on, pass keys=('k',) for GET-style commands.
  3. Review your cache config's cacheable-command list and remove commands you dispatch without keys.
  4. Report/fix the command path so keys are propagated to the cache proxy.

Example fix

# before (with caching on)
conn.send_command('GET', 'mykey')  # no keys kwarg
# after
conn.send_command('GET', 'mykey', keys=('mykey',))
Defensive patterns

Strategy: validation

Validate before calling

# When caching is on, route reads through the high-level API or pass keys
if caching_enabled:
    val = r.get('mykey')                 # client supplies keys
else:
    conn.send_command('GET', 'mykey')     # direct is fine without cache

# If you must call send_command directly with caching on:
conn.send_command('GET', 'mykey', keys=('mykey',))

Type guard

def has_keys_kwarg(kwargs: dict) -> bool:
    return kwargs.get('keys') is not None

Try / catch

try:
    conn.send_command('GET', 'mykey', keys=('mykey',))
except ValueError as e:
    if 'Cannot create cache key' in str(e):
        conn.send_command('GET', 'mykey', keys=('mykey',))
    else:
        raise

Prevention

When it happens

Trigger: The cache proxy decides a command is cacheable (a read command configured as cacheable) but send_command was invoked without the keys kwarg that the proxy uses to build CacheKey. Typically hit by the higher-level client layer failing to pass keys for a read command when caching is enabled.

Common situations: A read command routed through the cache proxy without keys metadata (custom command path, a command not yet plumbed to carry keys), or a misconfiguration of which commands are cacheable.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/4f2d6b101093029a. Report an issue: GitHub.

Appendix: source

Thrown at redis/connection.py:1780

        # next read_response does not try to cache their reply under a stale key.
        self._current_command_cache_key = None
        self._conn.send_packed_command(command)

    def send_command(self, *args, **kwargs):
        self._process_pending_invalidations()

        with self._cache_lock:
            # Command is write command or not allowed
            # to be cached.
            if not self._cache.is_cachable(
                CacheKey(command=args[0], redis_keys=(), redis_args=())
            ):
                self._current_command_cache_key = None
                self._conn.send_command(*args, **kwargs)
                return

        if kwargs.get("keys") is None:
            raise ValueError("Cannot create cache key.")

        # Creates cache key.
        self._current_command_cache_key = CacheKey(
            command=args[0], redis_keys=tuple(kwargs.get("keys")), redis_args=args
        )

        with self._cache_lock:
            # We have to trigger invalidation processing in case if
            # it was cached by another connection to avoid
            # queueing invalidations in stale connections.
            if self._cache.get(self._current_command_cache_key):
                entry = self._cache.get(self._current_command_cache_key)

                with self._pool_lock:
                    while entry.connection_ref.can_read():
                        try:
                            entry.connection_ref.read_response(
                                push_request=True,

View on GitHub (pinned to 6a6b581b48)