square/retrofit · error · IllegalStateException
Result must be parameterized as Result<Foo> or Result<? exte
Error message
Result must be parameterized as Result<Foo> or Result<? extends Foo>
What it means
Thrown by the RxJava3 call adapter factory when a service method returns an RxJava type parameterized with the raw retrofit2.adapter.rxjava3.Result class (no type argument). Result is Retrofit's wrapper that emits both Response and errors in the stream, but the factory needs the inner body type (line 146) to construct the adapter, so a raw Result is rejected at method-resolution time as an IllegalStateException.
Source
Thrown at retrofit-adapters/rxjava3/src/main/java/retrofit2/adapter/rxjava3/RxJava3CallAdapterFactory.java:143
+ " 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 Result with the body type: `Observable<Result<User>>`.
- If you do not need error-as-value semantics, use `Observable<User>` or `Observable<Response<User>>` instead.
- Remember Result only emits one item (Response or error) and never both, so the inner type is mandatory.
Example fix
// before
@GET("users/{id}")
Flowable<Result> getUser(@Path("id") String id);
// after
@GET("users/{id}")
Flowable<Result<User>> getUser(@Path("id") String id); Defensive patterns
Strategy: validation
Validate before calling
// Startup smoke test asserting Result is never used raw.
@Test void resultReturnTypesAreParameterized() throws Exception {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
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.adapter.rxjava3.Result.class, inner,
m.getName() + " uses raw Result; use Result<BodyType>");
}
}
} Try / catch
try {
MyService service = retrofit.create(MyService.class);
// exercise each method in a startup smoke test to trigger resolution
} catch (IllegalStateException e) {
throw new IllegalStateException(
"Retrofit service method return type is invalid: " + e.getMessage(), e);
} Prevention
- Always parameterize Result: Observable<Result<User>>.
- Remember Result<T> emits either a Response<T> or an error, never both, so the inner type is required.
- Do not confuse retrofit2.Response with the RxJava3 Result wrapper.
- Add a build-time test that resolves every service method return type.
When it happens
Trigger: A method declared as `@GET("x") Observable<Result> get();` (raw Result). The check at RxJava3CallAdapterFactory.java:141-145 fires when rawObservableType == Result.class but observableType is not a ParameterizedType.
Common situations: Developer switches from `Observable<User>` to the Result wrapper to capture HttpException/network errors in onNext but forgets the inner type; mixing up retrofit2.Response and the RxJava3 Result wrapper; copy-paste error; Kotlin generic elision.
Related errors
- Response must be parameterized as Response<Foo> or Response<
- ${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/91da2cc15c80a218.json.
Report an issue: GitHub.