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
For RxJava 1.x methods declared as Observable<Response<...>> (or Single<Response<...>>), the nested Response must be generic so the adapter can extract the body type. A raw Response gives no deserialization target, so RxJavaCallAdapterFactory throws IllegalStateException when the inner observableType is Response.class but not a ParameterizedType.
Source
Thrown at retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java:128
boolean isBody = false;
Type responseType;
if (!(returnType instanceof ParameterizedType)) {
String name = isSingle ? "Single" : "Observable";
throw new IllegalStateException(
name
+ " return type must be parameterized"
+ " as "
+ name
+ "<Foo> or "
+ name
+ "<? extends Foo>");
}
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
Class<?> rawObservableType = getRawType(observableType);
if (rawObservableType == Response.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException(
"Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
} else if (rawObservableType == Result.class) {
if (!(observableType instanceof ParameterizedType)) {
throw new IllegalStateException(
"Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>");
}
responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
isResult = true;
} else {
responseType = observableType;
isBody = true;
}
return new RxJavaCallAdapter(
responseType, scheduler, isAsync, isResult, isBody, isSingle, false);
}View on GitHub (pinned to d0b112dad0)
Solutions
- Parameterize Response: `Observable<Response<User>> getUser(@Path("id") long id);`
- Drop Response for body-only: `Observable<User> getUser(@Path("id") long id);`
Example fix
// before
Observable<Response> getUser(@Path("id") long id);
// after
Observable<Response<User>> getUser(@Path("id") long id); Defensive patterns
Strategy: validation
Validate before calling
import java.lang.reflect.*;
static void assertRxResponseParameterized(Class<?> iface) {
for (Method m : iface.getDeclaredMethods()) {
if (!(m.getGenericReturnType() instanceof ParameterizedType)) continue;
Type inner = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0];
if (inner.getTypeName().startsWith("retrofit2.Response") && !(inner instanceof ParameterizedType)) {
throw new AssertionError("Raw nested Response on " + m);
}
}
} Try / catch
try {
Observable<Response<User>> o = service.getUser(id);
} catch (IllegalStateException e) {
throw new IllegalStateException("Nested Response return type is raw: " + e.getMessage(), e);
} Prevention
- Fail builds on raw types (-Xlint:rawtypes).
- Unit-test that nested Response types are parameterized.
- Use body-only form unless you need status/headers.
When it happens
Trigger: A method like `Observable<Response> getUser();` — outer type parameterized, inner Response raw. Thrown after `getRawType(observableType) == Response.class` when observableType is not a ParameterizedType.
Common situations: Adding the Response wrapper to access status codes and forgetting the body generic; refactor dropping inner angle brackets.
Related errors
- Response must be parameterized as Response<Foo> or Response<
- Response must be parameterized as Response<Foo> or Response<
- ${name} return type must be parameterized as ${name}<Foo> or
- Result must be parameterized as Result<Foo> or Result<? exte
- Response must be parameterized as Response<Foo> or Response<
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/bd123cb68e8a3629.
Report an issue: GitHub.