{"id":"1b72fa2813ff41de","repo":"apache/kafka","slug":"kafkashareconsumer-methods-are-not-accessible-from","errorCode":null,"errorMessage":"KafkaShareConsumer methods are not accessible from user-defined acknowledgement commit callback.","messagePattern":"KafkaShareConsumer methods are not accessible from user-defined acknowledgement commit callback\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareConsumerImpl.java","lineNumber":1153,"sourceCode":"    }\n\n    /**\n     * Acquire the light lock protecting this consumer from multithreaded access. Instead of blocking\n     * when the lock is not available, however, we just throw an exception (since multithreaded usage is not\n     * supported).\n     *\n     * @throws ConcurrentModificationException if another thread already has the lock\n     */\n    private void acquire() {\n        final Thread thread = Thread.currentThread();\n        final long threadId = thread.getId();\n        if (threadId != currentThread.get() && !currentThread.compareAndSet(NO_CURRENT_THREAD, threadId))\n            throw new ConcurrentModificationException(\"KafkaShareConsumer is not safe for multi-threaded access. \" +\n                    \"currentThread(name: \" + thread.getName() + \", id: \" + threadId + \")\" +\n                    \" otherThread(id: \" + currentThread.get() + \")\"\n            );\n        if (acknowledgementCommitCallbackHandler != null && acknowledgementCommitCallbackHandler.hasEnteredCallback()) {\n            throw new IllegalStateException(\"KafkaShareConsumer methods are not accessible from user-defined \" +\n                    \"acknowledgement commit callback.\");\n        }\n        refCount.incrementAndGet();\n    }\n\n    /**\n     * Release the light lock protecting the consumer from multithreaded access.\n     */\n    private void release() {\n        if (refCount.decrementAndGet() == 0)\n            currentThread.set(NO_CURRENT_THREAD);\n    }\n\n    public static LogContext createLogContext(final String clientId, final String groupId) {\n        return new LogContext(\"[ShareConsumer clientId=\" + clientId + \", groupId=\" + groupId + \"] \");\n    }\n\n    private void maybeThrowInvalidGroupIdException() {","sourceCodeStart":1135,"sourceCodeEnd":1171,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ShareConsumerImpl.java#L1135-L1171","documentation":"Thrown by acquire() (line 1153) when acknowledgementCommitCallbackHandler.hasEnteredCallback() is true. It prevents re-entrancy: while the consumer is invoking the user-supplied acknowledgement commit callback, calling any ShareConsumer API method from inside that callback would corrupt in-flight acknowledgement state. The guard short-circuits such calls with an IllegalStateException.","triggerScenarios":"Inside a custom AcknowledgementCommitCallback, the code calls consumer.acknowledge(...), consumer.commitSync(...), consumer.poll(...), or any other public ShareConsumer method. Because the callback is dispatched on the consumer thread during handleCompletedAcknowledgements, the re-entrant acquire() detects the callback is in progress and throws.","commonSituations":"Developers treating the commit callback like an event listener and trying to drive further consumption from it (e.g. polling for more records or acknowledging again); porting code from a different MQ API where callback-driven acknowledgement chaining is idiomatic; logging/metrics hooks that accidentally call consumer.metrics() or other API methods.","solutions":["Do not call any ShareConsumer method from inside the AcknowledgementCommitCallback — only inspect the completed-acknowledgements data you are given.","If follow-up work is needed, hand it off to a separate executor or set a flag that the main poll loop checks.","Use the callback only for side effects that do not touch the consumer: external metrics, logging, business notifications.","Move the logic you attempted in the callback into the main consumer loop, executed after poll() returns."],"exampleFix":"// before\nconsumer.setAcknowledgementCommitCallback(acks -> {\n    consumer.commitSync(); // throws IllegalStateException\n});\n\n// after\nconsumer.setAcknowledgementCommitCallback(acks -> {\n    metrics.counter(\"acks.committed\").increment(acks.size());\n});","handlingStrategy":"validation","validationCode":"// Keep the acknowledgement commit callback pure: do not call consumer methods inside it.\norg.apache.kafka.clients.consumer.AcknowledgementCommitCallback cb = (acknowledgements, error) -> {\n    // Allowed: log, persist offsets to an external store, emit metrics.\n    // Forbidden here: consumer.poll(...), consumer.acknowledge(...), consumer.close(), etc.\n    if (error != null) log.warn(\"Ack commit failed\", error);\n};\nconsumer.setAcknowledgementCommitCallback(cb);","typeGuard":"null","tryCatchPattern":"// Defensive guard inside any helper that might be invoked from the callback.\nif (Thread.currentThread().getName().contains(\"ack-commit\")) {\n    throw new IllegalStateException(\"Refusing to call consumer methods from acknowledgement commit callback\");\n}","preventionTips":["Treat the acknowledgement commit callback as a pure notification handler; never call back into the consumer.","If processing is needed after a commit, enqueue work to the owner thread and let it call consumer methods.","Audit callback implementations during code review specifically for consumer method calls."],"tags":["share-consumer","acknowledgement","callback","reentrancy"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}