apache/kafka · error · IllegalStateException
Attempt to retrieve exception from future which hasn't faile
Error message
Attempt to retrieve exception from future which hasn't failed
What it means
Thrown by RequestFuture.exception() when the caller asks for the failure cause of a future that has not actually failed (it is either still incomplete or has completed successfully). The future's internal result is checked with failed() (result instanceof RuntimeException); if that is false, the future has no exception to hand back. This is a programmer misuse of the future's state machine, not a network or broker condition.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/RequestFuture.java:111
/**
* Check if the request is retriable. This is a convenience method for checking if
* the exception is an instance of {@link RetriableException}.
* @return true if it is retriable, false otherwise
* @throws IllegalStateException if the future is not complete or completed successfully
*/
public boolean isRetriable() {
return exception() instanceof RetriableException;
}
/**
* Get the exception from a failed result (only available if the request failed)
* @return the exception set in {@link #raise(RuntimeException)}
* @throws IllegalStateException if the future is not complete or completed successfully
*/
public RuntimeException exception() {
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();View on GitHub (pinned to c31c9215e1)
Solutions
- Guard every call to future.exception() (or future.isRetriable()) with if (future.failed()) { ... } else { ... }.
- If you only need the success path, use future.value() inside an if (future.succeeded()) block and never touch exception() on that branch.
- Audit custom RequestFutureListener and RequestFutureAdapter implementations, which are the usual sites that mishandle the success/failure split.
Example fix
// before
RequestFuture<ClientResponse> future = client.send(api, req);
client.poll(future);
RuntimeException cause = future.exception(); // throws if succeeded
// after
RequestFuture<ClientResponse> future = client.send(api, req);
client.poll(future);
if (future.failed()) {
throw future.exception();
}
ClientResponse response = future.value(); Defensive patterns
Strategy: type-guard
Validate before calling
// Always gate access to exception() with failed();
// never call future.exception() unconditionally.
if (future.isDone() && future.failed()) {
RuntimeException e = future.exception();
// handle e
} else {
// future is incomplete or succeeded; do not ask for an exception
} Type guard
// Narrow a RequestFuture to its failed branch before reading exception.
// Returns the exception only when the future has actually failed.
java.util.function.Function<RequestFuture<?>, java.util.Optional<RuntimeException>> failedException =
f -> f.isDone() && f.failed()
? java.util.Optional.of(f.exception())
: java.util.Optional.empty(); Try / catch
try {
RuntimeException e = future.exception();
} catch (IllegalStateException ex) {
// future hadn't failed; fall back to value() or await completion
if (!future.isDone()) future.awaitDone(timeout, TimeUnit.MILLISECONDS);
} Prevention
- Follow the javadoc contract: check succeeded()/failed() before calling value() or exception().
- Treat RequestFuture as a state machine — never assume the terminal state is FAILURE.
- Use the canonical pattern: poll(future) then branch on succeeded() before reading any result.
- Note RequestFuture is an internal class (clients.consumer.internals); prefer the higher-level KafkaConsumer API which already follows this contract.
When it happens
Trigger: Calling future.exception() without first checking future.failed() (or future.succeeded()/isDone()); calling exception() on a future that was completed via complete(value); calling isRetriable() on a future that succeeded (isRetriable() delegates to exception()).
Common situations: Custom RequestFutureListener.onFailure implementations that unconditionally call exception(); adapter code in compose()/chain() that assumes failure; debugging code that logs future.exception() without a succeeded()/failed() guard; refactors that move exception() calls out of the else-branch of a succeeded() check.
Related errors
- The argument to complete can not be an instance of RuntimeEx
- Invalid attempt to complete a request future which is alread
- The exception passed to raise must not be null
- Not authorized to access topics: ${unauthorizedTopics}
- Topic '${topic}' is invalid
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/b5796a6357aed063.json.
Report an issue: GitHub.