OpenFeign/feign · error · IllegalArgumentException
Method return type is not CompleteableFuture
Error message
Method return type is not CompleteableFuture: ${method} What it means
Validation in TargetSpecificationVerifier.verify for async targets: a method's return type is a subtype/specialization of CompletableFuture (e.g. CompletionStage or a custom subclass) rather than exactly CompletableFuture. The synchronous case is skipped, but Feign requires async methods to return exactly CompletableFuture so it can complete them; other async types cannot be fulfilled by the client.
Solutions
- Change the method's return type to CompletableFuture<T> exactly
- Use CompletionStage only outside the Feign interface and convert from CompletableFuture in wrapper code
- Remove the method from the async interface if it should be synchronous
- Check generic wrappers/builders that may have rewritten the declared return type
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at core/src/main/java/feign/ReflectiveFeign.java:246 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of OpenFeign/feign@e2a1e27560 (2026-09-10).
Data as JSON: /api/errors/b0356bed979756e0.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/feign/ReflectiveFeign.java:246
}
}
private static class TargetSpecificationVerifier {
public static <T> void verify(Target<T> target) {
Class<T> type = target.type();
if (!type.isInterface()) {
throw new IllegalArgumentException("Type must be an interface: " + type);
}
for (final Method m : type.getMethods()) {
final Class<?> retType = m.getReturnType();
if (!CompletableFuture.class.isAssignableFrom(retType)) {
continue; // synchronous case
}
if (retType != CompletableFuture.class) {
throw new IllegalArgumentException(
"Method return type is not CompleteableFuture: "
+ getFullMethodName(type, retType, m));
}
final Type genRetType = m.getGenericReturnType();
if (!(genRetType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Method return type is not parameterized: " + getFullMethodName(type, genRetType, m));
}
if (((ParameterizedType) genRetType).getActualTypeArguments()[0] instanceof WildcardType) {
throw new IllegalArgumentException(
"Wildcards are not supported for return-type parameters: "
+ getFullMethodName(type, genRetType, m));
}
}
}View on GitHub (pinned to e2a1e27560)