apache/kafka · error · IllegalStateException
Invalid attempt to complete a request future which is alread
Error message
Invalid attempt to complete a request future which is already complete
What it means
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.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java:128
if (!failed())
throw new IllegalStateException("Attempt to retrieve exception from future which hasn't failed");
return (RuntimeException) result.get();
}
/**
* Complete the request successfully. After this call, {@link #succeeded()} will return true
* and the value can be obtained through {@link #value()}.
* @param value corresponding value (or null if there is none)
* @throws IllegalStateException if the future has already been completed
* @throws IllegalArgumentException if the argument is an instance of {@link RuntimeException}
*/
public void complete(T value) {
try {
if (value instanceof RuntimeException)
throw new IllegalArgumentException("The argument to complete can not be an instance of RuntimeException");
if (!result.compareAndSet(INCOMPLETE_SENTINEL, value))
throw new IllegalStateException("Invalid attempt to complete a request future which is already complete");
fireSuccess();
} finally {
completedLatch.countDown();
}
}
/**
* Raise an exception. The request will be marked as failed, and the caller can either
* handle the exception or throw it.
* @param e corresponding exception to be passed to caller
* @throws IllegalStateException if the future has already been completed
*/
public void raise(RuntimeException e) {
try {
if (e == null)
throw new IllegalArgumentException("The exception passed to raise must not be null");
if (!result.compareAndSet(INCOMPLETE_SENTINEL, e))View on GitHub (pinned to c31c9215e1)
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().
Example fix
// before
future.complete(value); // may run after network layer already completed it
// after
if (!future.isDone()) {
future.complete(value);
} Defensive patterns
Strategy: validation
Validate before calling
// Never call complete() without confirming the future is still incomplete.
if (!future.isDone()) {
future.complete(value);
} else {
// already terminal; ignore or log — do not attempt to complete again
} Try / catch
try {
future.complete(value);
} catch (IllegalStateException ex) {
// future was already completed; safe to ignore since terminal state is already set
} Prevention
- 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().
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Attempt to retrieve exception from future which hasn't faile
- The argument to complete can not be an instance of RuntimeEx
- The exception passed to raise must not be null
- KafkaConsumer is not safe for multi-threaded access. current
- Not authorized to access topics: ${unauthorizedTopics}
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/cfcbbae29c1af9cc.json.
Report an issue: GitHub.