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 the RxJava3 call adapter factory while Retrofit resolves a service method's return type: the outer RxJava type (Observable/Flowable/Single/Maybe) is parameterized with the raw retrofit2.Response class instead of Response<T>. Because the factory must extract the body type to build a CallAdapter (see getParameterUpperBound at line 140), a raw Response has no body type to extract, so it fails fast. It surfaces as IllegalStateException the first time the offending method is invoked through the Retrofit proxy.
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 Response with the body type: `Observable<Response<User>> getUser(...)`.
- If you only need the body, drop the wrapper entirely: `Observable<User>`.
- If you want both success and error emitted into the stream, use `Observable<Result<User>>` instead.
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
// Startup smoke test: force resolution of each service method's return type
// so raw-Response misuse fails at build/init time, not at the first user request.
@Test void serviceReturnTypesAreParameterized() throws Exception {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
MyService svc = retrofit.create(MyService.class);
for (java.lang.reflect.Method m : MyService.class.getDeclaredMethods()) {
if (m.isDefault()) continue;
Type t = m.getGenericReturnType();
if (t instanceof java.lang.reflect.ParameterizedType) {
Type inner = ((java.lang.reflect.ParameterizedType) t).getActualTypeArguments()[0];
assertNotEquals(retrofit2.Response.class, inner,
m.getName() + " uses raw Response; use Response<BodyType>");
}
}
} Try / catch
// Wrap retrofit.create() / first invocation at app init to fail fast with a
// clear, attributable message. (Retrofit resolves return types lazily on
// first proxy invocation, so force it during startup validation.)
try {
MyService service = retrofit.create(MyService.class);
// touch each method's return-type resolution via your smoke test
} catch (IllegalStateException e) {
throw new IllegalStateException(
"Retrofit service method has an invalid return type: " + e.getMessage(), e);
} Prevention
- Always specify the inner type when wrapping with Response, e.g. Observable<Response<User>>.
- Keep a MockWebServer integration test that exercises every service method at build time.
- Treat IDE raw-type warnings as errors.
- In Kotlin, annotate platform-typed returns explicitly to avoid generic elision.
When it happens
Trigger: A service method declared as `@GET("users/{id}") Observable<Response> getUser(@Path("id") String id);` (raw Response, no type argument). The check at RxJava3CallAdapterFactory.java:135-139 fires when rawObservableType == Response.class but observableType is not a ParameterizedType.
Common situations: Developer wraps a working `Observable<User>` in Response to gain access to headers/status codes but forgets the inner type argument; IDE auto-imports the raw retrofit2.Response; copy-paste from samples that elided generics; Kotlin platform types where the generic was omitted.
Related errors
- Result must be parameterized as Result<Foo> or Result<? exte
- ${name} return type must be parameterized as ${name}<Foo> or
- Future return type must be parameterized as Future<Foo> or F
- 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/18ee2568241a0e5c.json.
Report an issue: GitHub.