quarkusio/quarkus · error · RedisKeyNotFoundException

The key `" + key + "` does not exist

Error message

The key `" + key + "` does not exist

What it means

decodeExpireResponse() interprets the reply of Redis EXPIRE-style commands. Redis returns -2 when the key does not exist; the Quarkus Redis client translates that into RedisKeyNotFoundException carrying the key name, instead of returning a misleading -2. This lets callers distinguish 'key gone' from a real expiration failure.

Source

Thrown at extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractKeyCommands.java:140

        cmd.put(marshaller.encode(key));
        cmd.put(timestamp);
        cmd.putArgs(expireArgs);
        return execute(cmd);
    }

    Uni<Response> _expireat(K key, Instant timestamp, ExpireArgs expireArgs) {
        return _expireat(key, timestamp.getEpochSecond(), expireArgs);
    }

    Uni<Response> _expiretime(K key) {
        nonNull(key, "key");
        return execute(RedisCommand.of(EXPIRETIME).put(marshaller.encode(key)));
    }

    long decodeExpireResponse(K key, Response r) {
        long res = r.toLong();
        if (res == -2) {
            throw new RedisKeyNotFoundException(new String(marshaller.encode(key), StandardCharsets.UTF_8));
        }
        return res;
    }

    Uni<Response> _keys(String pattern) {
        nonNull(pattern, "pattern");
        if (pattern.isBlank()) {
            return Uni.createFrom().failure(new IllegalArgumentException("`pattern` must not be blank"));
        }

        return execute(RedisCommand.of(Command.KEYS).put(pattern));
    }

    List<K> decodeKeys(Response response) {
        return marshaller.decodeAsList(response, typeOfKey);
    }

    Uni<Response> _move(K key, long db) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Handle RedisKeyNotFoundException in your Uni failure consumer and treat it as a non-fatal 'key missing' case.
  2. Check key existence first with the key commands (e.g. exists/key) if the race matters to your logic.
  3. Verify the key name/serialization matches what was actually written (prefixes, marshaller).

Example fix

// before
redis.expire(key, Duration.ofMinutes(5)).await().indefinitely();

// after
try {
    redis.expire(key, Duration.ofMinutes(5)).await().indefinitely();
} catch (RedisKeyNotFoundException e) {
    // key no longer exists; skip TTL update
}
Defensive patterns

Strategy: try-catch

Validate before calling

Boolean exists = redis.withConnection(c -> c.key().exists(key)).await().indefinitely();

Try / catch

try {
    redis.expire(key, ttl).await().indefinitely();
} catch (RedisKeyNotFoundException e) {
    // key does not exist; handle gracefully
}

Prevention

When it happens

Trigger: Calling expire/getExpire/pexpire-style key commands (e.g. ReactiveRedisDataSource.expire(key, ttl)) on a key that has been deleted or never existed; the server reply is -2.

Common situations: Race where another consumer/service deletes the key between existence check and EXPIRE; TTL already elapsed before the call; wrong key spelling or missing prefix in serialized key names.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/1ab38abe9aad3594. Report an issue: GitHub.