square/retrofit · error · IllegalStateException
Response must be parameterized as Response<Foo> or Response<
Error message
Response must be parameterized as Response<Foo> or Response<? extends Foo>
What it means
Thrown by Java8CallAdapterFactory when the CompletableFuture is parameterized but its argument is raw `Response` (e.g. `CompletableFuture<Response>`). The factory needs `Response<T>` to extract T for deserialization; the raw inner type fails the check at Java8CallAdapterFactory.java:79.
Source
Thrown at retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java:80
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);
}
private static final class BodyCallAdapter<R> implements CallAdapter<R, CompletableFuture<R>> {
private final Type responseType;
BodyCallAdapter(Type responseType) {
this.responseType = responseType;
}
@Override
public Type responseType() {
return responseType;
}
View on GitHub (pinned to d0b112dad0)
Solutions
- Parameterize the inner Response: `CompletableFuture<Response<User>> getUser();`.
- Or drop the Response wrapper entirely: `CompletableFuture<User> getUser();`.
- Prefer the built-in CompletableFuture support over the deprecated factory.
Example fix
// before
@GET("users/{id}")
CompletableFuture<Response> getUser(@Path("id") String id);
// after
@GET("users/{id}")
CompletableFuture<Response<User>> getUser(@Path("id") String id); Defensive patterns
Strategy: validation
Validate before calling
// Check for raw retrofit2.Response as a direct type argument of CompletableFuture.
for (Method m : MyService.class.getDeclaredMethods()) {
Type rt = m.getGenericReturnType();
if (rt instanceof ParameterizedType) {
Type arg = ((ParameterizedType) rt).getActualTypeArguments()[0];
if (arg == retrofit2.Response.class) {
throw new IllegalStateException(m + " uses raw Response inside CompletableFuture.");
}
}
} Try / catch
// Fail loud in a wiring test:
try {
retrofit.create(MyService.class).getUser("1");
} catch (IllegalStateException e) {
if (e.getMessage().contains("Response must be parameterized")) {
throw new IllegalStateException("Fix raw Response<> inside CompletableFuture in MyService.", e);
}
throw e;
} Prevention
- Always carry the body type when introducing Response<>.
- Drop the deprecated explicit factory if possible.
- Keep an end-to-end smoke test per service interface.
When it happens
Trigger: A service method returns `CompletableFuture<Response>` (raw Response) instead of `CompletableFuture<Response<Foo>>`.
Common situations: Switching a body return to a Response-wrapped return and dropping the inner type argument; autocompleting Response without generics; refactoring without re-running the wiring test.
Related errors
- CompletableFuture return type must be parameterized as Compl
- Response must be parameterized as Response<Foo> or Response<
- Response must be parameterized as Response<Foo> or Response<
- Response must be parameterized as Response<Foo> or Response<
- ListenableFuture return type must be parameterized as Listen
AI-assisted analysis of square/retrofit@d0b112dad0 (2026-08-04).
Data as JSON: /data/errors/c3adff7287a1108c.json.
Report an issue: GitHub.