{"id":"13c53d04805b21e8","repo":"apache/kafka","slug":"this-rebalanceconsumer-is-already-closed-re-use-o","errorCode":null,"errorMessage":"This RebalanceConsumer is already closed. Re-use of this object is not permitted","messagePattern":"This RebalanceConsumer is already closed\\. Re-use of this object is not permitted","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/DelegatingRebalanceConsumer.java","lineNumber":249,"sourceCode":"    public OptionalLong currentLag(TopicPartition topicPartition) {\n        ensureOpen();\n        return delegate.currentLag(topicPartition);\n    }\n\n    @Override\n    public ConsumerGroupMetadata groupMetadata() {\n        ensureOpen();\n        return delegate.groupMetadata();\n    }\n\n    @Override\n    public void close() {\n        isClosed = true;\n    }\n\n    private void ensureOpen() {\n        if (isClosed) {\n            throw new IllegalStateException(\n                    \"This RebalanceConsumer is already closed. Re-use of this object is not permitted\");\n        }\n    }\n}\n","sourceCodeStart":231,"sourceCodeEnd":254,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/DelegatingRebalanceConsumer.java#L231-L254","documentation":"IllegalStateException thrown by DelegatingRebalanceConsumer.ensureOpen() when any method is called after close() set isClosed=true. The RebalanceConsumer handle (obtained via KafkaConsumer.rebalanceConsumer() / ConsumerRebalanceListener) is single-use: once closed it must not be reused. This guards against stale references after the owning consumer or the rebalance listener context has been torn down.","triggerScenarios":"Calling any method on the RebalanceConsumer object (assignment, subscription, listTopics, currentLag, groupMetadata, etc.) after its close() was invoked — for example using a cached rebalanceConsumer reference inside a ConsumerRebalanceListener after the consumer closed.","commonSituations":"Storing the RebalanceConsumer in a field and reusing it across consumer lifecycles; calling rebalanceConsumer inside @KafkaListener tear-down after the consumer was already closed; frameworks that pool/recreate consumers while the listener holds a stale RebalanceConsumer.","solutions":["Stop invoking methods on the RebalanceConsumer after close(); obtain a fresh instance from the new KafkaConsumer.rebalanceConsumer().","Null out cached references to the RebalanceConsumer in ConsumerRebalanceListener.onPartitionsRevoked once close() is called.","Create a new KafkaConsumer rather than reusing a closed one."],"exampleFix":"// before\nRebalanceConsumer rc = consumer.rebalanceConsumer();\nconsumer.close();\nrc.subscription(); // IllegalStateException\n\n// after\nconsumer.close();\n// discard old reference\ntry (KafkaConsumer<K,V> c2 = new KafkaConsumer<>(props)) {\n    c2.rebalanceConsumer().subscription(); // fresh handle\n}","handlingStrategy":"validation","validationCode":"// RebalanceConsumer (DelegatingRebalanceConsumer) exposes no isOpen()/isClosed() getter.\n// Track lifecycle yourself in the owning object and guard every call:\nif (rebalanceConsumer == null || rebalanceConsumerClosed) {\n    throw new IllegalStateException(\"RebalanceConsumer is closed; obtain a new instance\");\n}\nrebalanceConsumer.committed(...); // safe to use","typeGuard":"// Optional state wrapper that makes 'closed' visible in the type system.\nfinal class OpenRebalanceConsumer {\n    private final RebalanceConsumer delegate;\n    private OpenRebalanceConsumer(RebalanceConsumer c) { this.delegate = c; }\n    static OpenRebalanceConsumer open(RebalanceConsumer c) {\n        return new OpenRebalanceConsumer(c);\n    }\n    RebalanceConsumer get() { return delegate; } // only callable while wrapper exists\n}\n// Closing invalidates the wrapper (drop the reference); a fresh open() is required to use it again.","tryCatchPattern":"try {\n    rebalanceConsumer.poll(...);\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"already closed\")) {\n        // DelegatingRebalanceConsumer.ensureOpen() fired at line 249.\n        // Discard this reference and obtain a new consumer from KafkaConsumer.\n        log.warn(\"RebalanceConsumer reused after close; discarding\");\n        rebalanceConsumer = null; // force re-acquisition\n    } else {\n        throw e;\n    }\n}","preventionTips":["Treat RebalanceConsumer as single-use per consumer lifecycle; never cache it past KafkaConsumer.close().","In a rebalance listener, do not call methods on a RebalanceConsumer you received before a previous close.","Set the field to null immediately after closing so any later call fails fast with an NPE you control rather than the library's IllegalStateException.","Avoid sharing the consumer (and its RebalanceConsumer view) across threads; the closed flag races are all your own."],"tags":["consumer","lifecycle","illegal-state","rebalance"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}