lysine-dev/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 ScalaCallAdapterFactory.get() when the method returns Future<Response> with a raw inner Response. The factory resolved the outer Future's type argument (innerType) and found it is Response.class, so it now requires innerType itself to be a ParameterizedType so it can extract the concrete body type to deserialize. A raw Response yields no body type.
Source
Thrown at retrofit-adapters/scala/src/main/java/retrofit2/adapter/scala/ScalaCallAdapterFactory.java:76
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Future.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"Future return type must be parameterized as Future<Foo> or Future<? 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);
}
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 inner Response: change Future<Response> to Future<Response<User>>.
- If you only need the deserialized body, drop Response entirely and return Future<User>.
- Recompile so the corrected signature is re-evaluated when Retrofit builds the call adapter.
Example fix
// before
@GET("users/{id}")
Future<Response> getUser(@Path("id") String id);
// after
@GET("users/{id}")
Future<Response<User>> getUser(@Path("id") String id); Defensive patterns
Strategy: validation
Validate before calling
// Signature discipline; no per-call pre-check. Catch at Retrofit.create() in a unit test.
// Static check that an outer Future<Response> carries a parameterized Response:
Method m = ApiService.class.getMethod("getUser", String.class);
Type rt = m.getGenericReturnType();
if (rt instanceof ParameterizedType) {
Type inner = ((ParameterizedType) rt).getActualTypeArguments()[0];
if (inner == retrofit2.Response.class) {
throw new AssertionError("getUser() returns Future<Response>; parameterize the Response.");
}
} Type guard
// Enforce in Scala/Java by always declaring the inner type explicitly: // Future<Response<User>> rather than Future<Response>.
Try / catch
try {
ApiService api = retrofit.create(ApiService.class);
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("Response must be parameterized")) {
throw new ConfigurationException("Service interface has Future<Response> with raw Response", e);
}
throw e;
} Prevention
- When wrapping a body type in Future<Response<...>>, always supply the body generic.
- Treat raw retrofit2.Response in any signature as a compile-time smell.
- Unit-test Retrofit.create() against the Scala-backed service to catch this early.
When it happens
Trigger: A Retrofit interface method declared as Future<Response> getUser(...) — the outer Future is parameterized but its argument is the raw retrofit2.Response class. After the `getRawType(innerType) != Response.class` check passes, the `innerType instanceof ParameterizedType` check fails and throws.
Common situations: Wanting access to headers/status code via Response but forgetting the body generic, or converting a Future<User> method to Future<Response> during a refactor and stopping one level short.
Related errors
- Future return type must be parameterized as Future<Foo> or F
- Response must be parameterized as Response<Foo> or Response<
- Result must be parameterized as Result<Foo> or Result<? exte
- gson == null
- mapper == null
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/b6cad7296bf72864.
Report an issue: GitHub.