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

  1. Make the fallback return the same execution type as the command — plain User for a sync command
  2. Keep separate fallback methods for the sync and async variants rather than sharing one
  3. Add a unit test that loads each command/fallback pair once so the definition validation runs in CI
  4. 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

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


AI-assisted analysis of Netflix/Hystrix@5ce3bc58c3 (2026-08-14). Data as JSON: /api/errors/dae7d0a4b375e029. Report an issue: GitHub.