{"record":{"id":"73302e7813a5a406","repo":"redisson/redisson","slug":"subscribe-through-reactivesubscription-object-crea-73302e","errorCode":null,"errorMessage":"Subscribe through ReactiveSubscription object created by createSubscription method","messagePattern":"Subscribe through ReactiveSubscription object created by createSubscription method","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"redisson-spring/redisson-spring-data/redisson-spring-data-25/src/main/java/org/redisson/spring/data/connection/RedissonReactivePubSubCommands.java","lineNumber":56,"sourceCode":"    RedissonReactivePubSubCommands(CommandReactiveExecutor executorService) {\n        super(executorService);\n    }\n\n    @Override\n    public Mono<ReactiveSubscription> createSubscription() {\n        return Mono.just(new RedissonReactiveSubscription(executorService.getConnectionManager()));\n    }\n\n    @Override\n    public Flux<Long> publish(Publisher<ChannelMessage<ByteBuffer, ByteBuffer>> messageStream) {\n        return execute(messageStream, msg -> {\n            return write(toByteArray(msg.getChannel()), StringCodec.INSTANCE, RedisCommands.PUBLISH, toByteArray(msg.getChannel()), toByteArray(msg.getMessage()));\n        });\n    }\n\n    @Override\n    public Mono<Void> subscribe(ByteBuffer... channels) {\n        throw new UnsupportedOperationException(\"Subscribe through ReactiveSubscription object created by createSubscription method\");\n    }\n\n    @Override\n    public Mono<Void> pSubscribe(ByteBuffer... patterns) {\n        throw new UnsupportedOperationException(\"Subscribe through ReactiveSubscription object created by createSubscription method\");\n    }\n\n}\n","sourceCodeStart":38,"sourceCodeEnd":65,"githubUrl":"https://github.com/redisson/redisson/blob/91188987c2a9023e7fedabff758681ad9b107f25/redisson-spring/redisson-spring-data/redisson-spring-data-25/src/main/java/org/redisson/spring/data/connection/RedissonReactivePubSubCommands.java#L38-L65","documentation":"Redisson's implementation of the Spring Data Redis reactive API deliberately does not support the one-shot subscribe(ByteBuffer...) method on ReactiveRedisConnection.ReactivePubSubCommands. Subscription state (channels, patterns, listeners) must live in a ReactiveSubscription object, because Redisson routes pub/sub messages through its own connection manager event loop. Calling subscribe directly would create state Redisson cannot track, so it throws UnsupportedOperationException to redirect you to createSubscription().","triggerScenarios":"Calling RedissonReactivePubSubCommands.subscribe(channels) directly, e.g. connection.pubSubCommands().subscribe(ByteBuffer.wrap(\"ch\".getBytes())). Also hit when application code or a framework adapter calls the ReactivePubSubCommands interface method instead of obtaining a subscription object.","commonSituations":"Migrating from Lettuce/Jedis reactive adapters to Redisson where subscribe() on the commands object used to work; test code that exercises the ReactivePubSubCommands interface directly; higher-level code that assumes all ReactivePubSubCommands methods are implemented.","solutions":["Call createSubscription() to get a RedissonReactiveSubscription, then call subscribe(channels) on that object","Register listeners via subscription.receive() / receiveLater() before subscribing so messages are not missed","Keep a reference to the subscription and close it (subscription.close()) when done, since channels stay subscribed until then"],"exampleFix":"// before\nFlux<ByteBuffer> msgs = connection.pubSubCommands()\n    .subscribe(ByteBuffer.wrap(\"channel\".getBytes())) // throws\n    .thenMany(Flux.empty());\n\n// after\nReactiveRedisConnection.ReactiveSubscription sub =\n    connection.pubSubCommands().createSubscription();\n\nFlux<ByteBufferMessage> msgs = sub.receive()\n    .doOnSubscribe(s -> sub.subscribe(ByteBuffer.wrap(\"channel\".getBytes())).subscribe());\n\n// ... and on shutdown:\nsub.close();","handlingStrategy":"validation","validationCode":"// Before subscribing, obtain the subscription object — never call the\n// one-shot subscribe on ReactivePubSubCommands:\nReactiveRedisConnection.ReactiveSubscription sub =\n    connection.pubSubCommands().createSubscription();\nif (sub != null) {\n    sub.receive().doOnNext(this::onMessage).subscribe();\n    sub.subscribe(ByteBuffer.wrap(channel.getBytes())).subscribe();\n}","typeGuard":null,"tryCatchPattern":"try {\n    connection.pubSubCommands().subscribe(channels); // unsupported\n} catch (UnsupportedOperationException e) {\n    // fall back to subscription-object API\n    ReactiveRedisConnection.ReactiveSubscription sub =\n        connection.pubSubCommands().createSubscription();\n    sub.receive().doOnNext(this::onMessage).subscribe();\n    sub.subscribe(channels).subscribe();\n}","preventionTips":["Treat createSubscription() as the only entry point for reactive pub/sub in Redisson","Store the ReactiveSubscription in a field and close it on bean destruction to avoid leaked subscriptions","Add an integration test that exercises your subscribe path so the UnsupportedOperationException is caught in CI, not production"],"tags":["redis","redisson","spring-data-redis","reactive","pubsub","unsupported-operation"],"backgroundTag":null,"analyzedSha":"91188987c2a9023e7fedabff758681ad9b107f25","analyzedAt":"2026-08-14T11:39:42.619Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}