square/retrofit · error · IllegalStateException
CompletableFuture return type must be parameterized as Compl
Error message
CompletableFuture return type must be parameterized as CompletableFuture<Foo> or CompletableFuture<? extends Foo>
What it means
Thrown by the (now-deprecated) Java8CallAdapterFactory while inspecting a service method's return type. A raw `CompletableFuture` gives the factory no body type to deserialize into, so at Java8CallAdapterFactory.java:67 it rejects the raw type. Note: Retrofit now bundles CompletableFuture support, so adding this factory explicitly is unnecessary.
Source
Thrown at retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java:67
* errors
* </ul>
*/
@Deprecated
public final class Java8CallAdapterFactory extends CallAdapter.Factory {
public static Java8CallAdapterFactory create() {
return new Java8CallAdapterFactory();
}
private Java8CallAdapterFactory() {}
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != CompletableFuture.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"CompletableFuture return type must be parameterized"
+ " as CompletableFuture<Foo> or CompletableFuture<? extends Foo>");
}
Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);
if (getRawType(innerType) != Response.class) {
// Generic type is not Response<T>. Use it for body-only adapter.
return new BodyCallAdapter<>(innerType);
}
// Generic type is Response<T>. Extract T and create the Response version of the adapter.
if (!(innerType instanceof ParameterizedType)) {
throw new IllegalStateException(
"Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);
return new ResponseCallAdapter<>(responseType);
}View on GitHub (pinned to d0b112dad0)
Solutions
- Parameterize the return: `CompletableFuture<User> getUser();`.
- Use `CompletableFuture<Response<User>>` if you need the full HTTP Response.
- Since bundled support exists, remove the explicit Java8CallAdapterFactory from Retrofit.Builder unless you depend on the deprecated class -- Retrofit handles CompletableFuture natively.
Example fix
// before
@GET("users/{id}")
CompletableFuture getUser(@Path("id") String id);
// after
@GET("users/{id}")
CompletableFuture<User> getUser(@Path("id") String id); Defensive patterns
Strategy: validation
Validate before calling
// javac -Xlint:rawtypes -Werror catches raw CompletableFuture at compile time.
// Reflection guard for completeness:
for (Method m : MyService.class.getDeclaredMethods()) {
if (m.getGenericReturnType() == java.util.concurrent.CompletableFuture.class) {
throw new IllegalStateException(m + " returns raw CompletableFuture.");
}
} Try / catch
// Fail fast during a wiring/self-test rather than at production call time:
try {
retrofit.create(MyService.class).getUser("1");
} catch (IllegalStateException e) {
if (e.getMessage().contains("CompletableFuture return type")) {
throw new IllegalStateException("Raw CompletableFuture in MyService; parameterize it.", e);
}
throw e;
} Prevention
- Do not add the deprecated Java8CallAdapterFactory -- rely on built-in support.
- Never suppress raw-types warnings on Retrofit return types.
- Write a smoke test per service interface that invokes one method.
When it happens
Trigger: A service interface method returns raw `CompletableFuture` (no type argument): `@GET("/") CompletableFuture getData();`. The check `returnType instanceof ParameterizedType` at line 67 fails.
Common situations: Migrating to Java 8 futures and forgetting the generic; or explicitly adding the deprecated Java8CallAdapterFactory while writing a first method. Using a raw type to silence a compiler warning. Surfaces on first method invocation because Retrofit validates lazily.
Related errors
- Response must be parameterized as Response<Foo> or Response<
- ListenableFuture return type must be parameterized as Listen
- Response must be parameterized as Response<Foo> or Response<
- ${name} return type must be parameterized as ${name}<Foo> or
- ${name} return type must be parameterized as ${name}<Foo> or
AI-assisted analysis of square/retrofit@d0b112dad0 (2026-08-04).
Data as JSON: /data/errors/ab200ed523a7b23a.json.
Report an issue: GitHub.