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 RxJava3CallAdapterFactory while resolving the CallAdapter for a service method whose return type's type argument is a raw retrofit2.Response. The factory unwraps the outer reactive type (Observable/Flowable/Single/Maybe) via getParameterUpperBound, and when that bound is Response.class it requires Response itself to carry a concrete body type. A bare Response gives the adapter no body type to deserialize into, so it refuses to build the adapter.
Source
Thrown at retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java:137
Type responseType;
if (!(returnType instanceof ParameterizedType)) {
String name =
isFlowable ? "Flowable" : isSingle ? "Single" : isMaybe ? "Maybe" : "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 RxJava3CallAdapter(
responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
}View on GitHub (pinned to d0b112dad0)
Solutions
- Parameterize the Response in the return type, e.g. change Observable<Response> to Observable<Response<User>>.
- If you want the body only, drop Response entirely and return Observable<User>.
- If you want both body and errors as a stream, switch to Observable<Result<User>> which the factory also supports.
- Clean/rebuild the project so the corrected interface is recompiled before Retrofit.validateEagerly() inspects it.
Example fix
// before
@GET("users/{id}")
Observable<Response> getUser(@Path("id") String id);
// after
@GET("users/{id}")
Observable<Response<User>> getUser(@Path("id") String id); Defensive patterns
Strategy: validation
Validate before calling
// Verify the service method signature at build time before Retrofit scans it.
// In Java there is no runtime pre-check; the guard is compile-time discipline:
// - never write Observable<Response> / Flowable<Response> / Single<Response> / Maybe<Response>
// - always supply the body type: Observable<Response<BodyType>>
//
// Optional unit test that fails fast if a signature drifts to raw Response:
Method m = ApiService.class.getMethod("getUser", String.class);
Type rt = m.getGenericReturnType();
if (rt instanceof ParameterizedType) {
Type arg = ((ParameterizedType) rt).getActualTypeArguments()[0];
if (arg == retrofit2.Response.class) {
throw new AssertionError("getUser() returns raw Observable<Response>; parameterize it.");
}
} Type guard
// Kotlin reified helper to enforce Response is parameterized at the call site.
inline fun <reified T> responseObservableType(): Type =
TypeToken<Observable<Response<T>>>() {}.type // compile error if T omitted Try / catch
// This error fires at Retrofit.create()/build() time, not per-call.
// Prefer fixing the signature; if you must isolate it, catch during service creation:
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 a raw Observable<Response> method", e);
}
throw e;
} Prevention
- Always parameterize retrofit2.Response with a body type in reactive return signatures.
- Enable IDE raw-types/unchecked warnings as errors so a raw Response surfaces immediately.
- Add a unit test that calls Retrofit.create() with eager validation to catch signature errors before integration.
When it happens
Trigger: A Retrofit interface method declared as Observable<Response>, Flowable<Response>, Single<Response>, or Maybe<Response> (raw Response, no generic argument). The factory's get() is invoked at Retrofit.build() time when it scans the service interface; the check `observableType instanceof ParameterizedType` fails for the raw class and throws IllegalStateException.
Common situations: Copying a method signature and forgetting to parameterize Response, refactoring a method that returned Observable<User> into Observable<Response> to gain access to headers/status, or IDE auto-import picking the raw retrofit2.Response type and silently dropping the generic.
Related errors
- Result must be parameterized as Result<Foo> or Result<? exte
- Future return type must be parameterized as Future<Foo> or F
- Response must be parameterized as Response<Foo> or Response<
- error == null
- response == null
AI-assisted analysis of lysine-dev/retrofit@d0b112dad0 (2026-08-13).
Data as JSON: /api/errors/b0485cf41357d9f9.
Report an issue: GitHub.