Netflix/Hystrix · error · FallbackDefinitionException
fallback cannot return Future if command isn't asynchronous.
Error message
fallback cannot return Future if command isn't asynchronous.
What it means
The mirror rule of the async case: when the command's execution type is NOT asynchronous (synchronous or Observable), a fallback that returns Future is rejected with FallbackDefinitionException — Javanica will not block on a Future to produce a sync/observable result, so the signature pairing is illegal by definition.
Source
Thrown at hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/utils/FallbackMethod.java:133
} else if (ExecutionType.ASYNCHRONOUS == ExecutionType.getExecutionType(commandReturnType)) {
if (isCommand() && ExecutionType.ASYNCHRONOUS == getExecutionType()) {
validateReturnType(commandMethod, method);
}
if (ExecutionType.ASYNCHRONOUS != getExecutionType()) {
Type commandParametrizedType = commandMethod.getGenericReturnType();
if (isReturnTypeParametrized(commandMethod)) {
commandParametrizedType = getFirstParametrizedType(commandMethod);
}
validateParametrizedType(commandParametrizedType, method.getGenericReturnType(), commandMethod, method);
}
if (!isCommand() && ExecutionType.ASYNCHRONOUS == getExecutionType()) {
throw new FallbackDefinitionException(createErrorMsg(commandMethod, method, "fallback cannot return Future if the fallback isn't command when the command is async."));
}
} else {
if (ExecutionType.ASYNCHRONOUS == getExecutionType()) {
throw new FallbackDefinitionException(createErrorMsg(commandMethod, method, "fallback cannot return Future if command isn't asynchronous."));
}
if (ExecutionType.OBSERVABLE == getExecutionType()) {
throw new FallbackDefinitionException(createErrorMsg(commandMethod, method, "fallback cannot return Observable if command isn't observable."));
}
validateReturnType(commandMethod, method);
}
}
}
private Type getFirstParametrizedType(Method m) {
Type gtype = m.getGenericReturnType();
if (gtype instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) gtype;
return pType.getActualTypeArguments()[0];
}
return null;
}View on GitHub (pinned to 5ce3bc58c3)
Solutions
- Make the fallback return the same execution type as the command — plain User for a sync command
- Keep separate fallback methods for the sync and async variants rather than sharing one
- Add a unit test that loads each command/fallback pair once so the definition validation runs in CI
- Consult the Javanica fallback signature matrix (sync->sync, async->plain value or command, observable->observable or plain value)
Example fix
// before
@HystrixCommand
public User getUser(String id) { ... }
private Future<User> getUserFallback(String id) { ... }
// after
@HystrixCommand
public User getUser(String id) { ... }
private User getUserFallback(String id) { ... } Defensive patterns
Strategy: validation
Validate before calling
Class<?> cmdRet = commandMethod.getReturnType();
Class<?> fbRet = fallbackMethod.getReturnType();
boolean cmdAsync = Future.class.isAssignableFrom(cmdRet);
if (!cmdAsync && Future.class.isAssignableFrom(fbRet)) {
throw new IllegalStateException("Non-async command cannot have a Future-returning fallback: " + fallbackMethod);
} Try / catch
catch (FallbackDefinitionException e) { log.error("Fallback returns Future for non-async command: {}", e.getMessage()); failBuild(e); } Prevention
- Match fallback execution type to command execution type by convention
- Separate fallbacks per command variant; do not share across sync/async overloads
- CI test that loads every annotated bean to trigger validation
When it happens
Trigger: @HystrixCommand User getUser() (sync) paired with fallback Future<User> getUserFallback(); or an Observable-returning command whose fallback returns Observable<Future<User>>-style async types.
Common situations: Converting a command from async back to sync and leaving the fallback async; sharing one fallback method between sync and async overloads of a command; refactoring return types without updating the fallback.
Related errors
- fallback cannot return Future if the fallback isn't command
- fallback cannot return Observable if command isn't observabl
- fallback cannot return 'void' if command return type is " +
- Incompatible return types. \nCommand method: " + commandMeth
- Incompatible return types. \nCommand method: " + commandMeth
AI-assisted analysis of Netflix/Hystrix@5ce3bc58c3 (2026-08-14).
Data as JSON: /api/errors/dae7d0a4b375e029.
Report an issue: GitHub.