{"id":"cfcbbae29c1af9cc","repo":"apache/kafka","slug":"invalid-attempt-to-complete-a-request-future-which","errorCode":null,"errorMessage":"Invalid attempt to complete a request future which is already complete","messagePattern":"Invalid attempt to complete a request future which is already complete","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java","lineNumber":128,"sourceCode":"        if (!failed())\n            throw new IllegalStateException(\"Attempt to retrieve exception from future which hasn't failed\");\n        return (RuntimeException) result.get();\n    }\n\n    /**\n     * Complete the request successfully. After this call, {@link #succeeded()} will return true\n     * and the value can be obtained through {@link #value()}.\n     * @param value corresponding value (or null if there is none)\n     * @throws IllegalStateException if the future has already been completed\n     * @throws IllegalArgumentException if the argument is an instance of {@link RuntimeException}\n     */\n    public void complete(T value) {\n        try {\n            if (value instanceof RuntimeException)\n                throw new IllegalArgumentException(\"The argument to complete can not be an instance of RuntimeException\");\n\n            if (!result.compareAndSet(INCOMPLETE_SENTINEL, value))\n                throw new IllegalStateException(\"Invalid attempt to complete a request future which is already complete\");\n            fireSuccess();\n        } finally {\n            completedLatch.countDown();\n        }\n    }\n\n    /**\n     * Raise an exception. The request will be marked as failed, and the caller can either\n     * handle the exception or throw it.\n     * @param e corresponding exception to be passed to caller\n     * @throws IllegalStateException if the future has already been completed\n     */\n    public void raise(RuntimeException e) {\n        try {\n            if (e == null)\n                throw new IllegalArgumentException(\"The exception passed to raise must not be null\");\n\n            if (!result.compareAndSet(INCOMPLETE_SENTINEL, e))","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java#L110-L146","documentation":"Thrown by RequestFuture.complete(T value) when the CAS result.compareAndSet(INCOMPLETE_SENTINEL, value) fails because the future already holds a terminal value (either a success value or a failure exception). A RequestFuture is single-shot; once complete() or raise() has run, the result cannot be overwritten. Attempting to complete it again indicates a double-completion bug in the calling code.","triggerScenarios":"Two code paths racing to complete the same future (e.g. a success callback and a timeout handler firing in the same poll loop); a listener registered via addListener() that calls complete() on the already-completed source; manually calling complete() after the network layer already completed the future.","commonSituations":"Adding a deadline/timeout future completion on top of the existing network completion without deduplication; chaining futures with chain() or compose() and then also completing the source; refactors that surface a second completion site.","solutions":["Gate every completion site on if (!future.isDone()) before calling complete() (or use compareAndSet yourself).","Eliminate the second completion path; a future should have exactly one owner that completes it.","If a timeout path is needed, cancel the in-flight request and complete once from a single winsite guarded by isDone()."],"exampleFix":"// before\nfuture.complete(value); // may run after network layer already completed it\n\n// after\nif (!future.isDone()) {\n    future.complete(value);\n}","handlingStrategy":"validation","validationCode":"// Never call complete() without confirming the future is still incomplete.\nif (!future.isDone()) {\n    future.complete(value);\n} else {\n    // already terminal; ignore or log — do not attempt to complete again\n}","typeGuard":null,"tryCatchPattern":"try {\n    future.complete(value);\n} catch (IllegalStateException ex) {\n    // future was already completed; safe to ignore since terminal state is already set\n}","preventionTips":["Check isDone() before every complete() / raise() call.","Avoid fanning out the same future to multiple completers; ensure a single owner.","Use the static factories failure()/voidSuccess() for one-shot creation instead of complete()-after-construction.","In listener callbacks, do not re-complete the source future; chain into a new future via compose()/chain()."],"tags":["consumer","async","future-state","concurrency"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}