redisson/redisson · error · CacheException

Unable to locate Redisson instance by name: ${jndiName}

Error message

Unable to locate Redisson instance by name: ${jndiName}

What it means

RedissonConnection.scan() iterates master entries directly over raw connections, producing a live Cursor. Cursors require request/response round-trips per batch, so Redisson refuses to run SCAN while the connection is in MULTI queueing or pipeline mode, throwing UnsupportedOperationException from doScan.

Source

Thrown at redisson-hibernate/redisson-hibernate-4/src/main/java/org/redisson/hibernate/JndiRedissonRegionNativeFactory.java:53

    private static final long serialVersionUID = -4814502675083325567L;

    public static final String JNDI_NAME = CONFIG_PREFIX + "jndi_name";
    
    @Override
    protected RedissonClient createRedissonClient(Properties properties) {
        String jndiName = ConfigurationHelper.getString(JNDI_NAME, properties);
        if (jndiName == null) {
            throw new CacheException(JNDI_NAME + " property not set");
        }
        
        Properties jndiProperties = JndiServiceImpl.extractJndiProperties(properties);
        InitialContext context = null;
        try {
            context = new InitialContext(jndiProperties);
            return (RedissonClient) context.lookup(jndiName);
        } catch (NamingException e) {
            throw new CacheException("Unable to locate Redisson instance by name: " + jndiName, e);
        } finally {
            if (context != null) {
                try {
                    context.close();
                } catch (NamingException e) {
                    throw new CacheException("Unable to close JNDI context", e);
                }
            }
        }
    }

    @Override
    public void stop() {
    }

}

View on GitHub (pinned to 91188987c2)

Solutions

  1. Run scan outside any pipeline/transaction: use RedisTemplate.scan(options) or a raw connection cursor on a non-pipelined connection.
  2. Collect keys with SCAN first, then pipeline the follow-up operations on those keys.
  3. If a single blocking call is acceptable in pipeline mode, use conn.keys(pattern) instead of scan.

Example fix

// before
redisTemplate.executePipelined((RedisCallback<Object>) conn -> {
    Cursor<byte[]> c = conn.scan(ScanOptions.scanOptions().match("sess:*").build());
    c.forEachRemaining(k -> redisTemplate.delete(new String(k))); // throws inside pipeline
    return null;
});

// after
try (Cursor<byte[]> c = redisTemplate.scan(ScanOptions.scanOptions().match("sess:*").build())) {
    List<String> keys = c.stream().map(String::new).collect(Collectors.toList());
    if (!keys.isEmpty()) redisTemplate.delete(keys);
}
Defensive patterns

Strategy: validation

Validate before calling

if (conn.isPipelined() || conn.isQueueing()) {
    throw new IllegalStateException("SCAN not allowed here; collect keys first, pipeline later");
}
try (Cursor<byte[]> c = conn.scan(options)) { ... }

Try / catch

catch (UnsupportedOperationException e) where message contains "pipeline / transaction mode": move the scan to a fresh connection obtained outside executePipelined and re-run.

Prevention

When it happens

Trigger: Calling conn.scan(ScanOptions) inside RedisTemplate.executePipelined(...) or inside a multi()/exec() SessionCallback. The ScanCursor is lazily evaluated, so the exception surfaces when the cursor is first advanced or closed (inside the pipeline callback), not when scan() is called.

Common situations: Wrapping RedisTemplate.scan(cursor) in executePipelined hoping to batch results; calling scan within RedisTransactionalSessionCallback; frameworks that implicitly pipeline callbacks (e.g. some RedisTemplate operations inside executePipelined).

Related errors


AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14). Data as JSON: /api/errors/946599ea3ab096fe. Report an issue: GitHub.