{"record":{"id":"b39ec051066992cc","repo":"Netflix/Hystrix","slug":"fallback-cannot-return-void-if-command-return-ty","errorCode":null,"errorMessage":"fallback cannot return 'void' if command return type is \" + Completable.class.getSimpleName()","messagePattern":"fallback cannot return 'void' if command return type is \" \\+ Completable\\.class\\.getSimpleName\\(\\)","errorType":"exception","errorClass":"FallbackDefinitionException","httpStatus":null,"severity":"error","filePath":"hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/utils/FallbackMethod.java","lineNumber":156,"sourceCode":"                validateReturnType(commandMethod, method);\n            }\n\n        }\n    }\n\n    private Type getFirstParametrizedType(Method m) {\n        Type gtype = m.getGenericReturnType();\n        if (gtype instanceof ParameterizedType) {\n            ParameterizedType pType = (ParameterizedType) gtype;\n            return pType.getActualTypeArguments()[0];\n        }\n        return null;\n    }\n\n    // everything can be wrapped into completable except 'void'\n    private void validateCompletableReturnType(Method commandMethod, Class<?> callbackReturnType) {\n        if (Void.TYPE == callbackReturnType) {\n            throw new FallbackDefinitionException(createErrorMsg(commandMethod, method, \"fallback cannot return 'void' if command return type is \" + Completable.class.getSimpleName()));\n        }\n    }\n\n    private void validateReturnType(Method commandMethod, Method fallbackMethod) {\n        if (isGenericReturnType(commandMethod)) {\n            List<Type> commandParametrizedTypes = flattenTypeVariables(commandMethod.getGenericReturnType());\n            List<Type> fallbackParametrizedTypes = flattenTypeVariables(fallbackMethod.getGenericReturnType());\n            Result result = equalsParametrizedTypes(commandParametrizedTypes, fallbackParametrizedTypes);\n            if (!result.success) {\n                List<String> msg = new ArrayList<String>();\n                for (Error error : result.errors) {\n                    Optional<Type> parentKindOpt = getParentKind(error.commandType, commandParametrizedTypes);\n                    String extraHint = \"\";\n                    if (parentKindOpt.isPresent()) {\n                        Type parentKind = parentKindOpt.get();\n                        if (isParametrizedType(parentKind)) {\n                            extraHint = \"--> \" + ((ParameterizedType) parentKind).getRawType().toString() + \"<Ooops!>\\n\";\n                        }","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/Netflix/Hystrix/blob/5ce3bc58c38e7ca60ef2fe0e516e390e294ad941/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/utils/FallbackMethod.java#L138-L174","documentation":"When the @HystrixCommand method returns rx.Completable, Javanica permits the fallback to return any type that can wrap completion ('everything can be wrapped into completable') EXCEPT void. validateCompletableReturnType throws FallbackDefinitionException if the fallback's return type is Void.TYPE (primitive void), since there would be no value to complete the Completable with and no way to signal a fallback result.","triggerScenarios":"@HystrixCommand Completable doWork() with a fallback declared as `void doWorkFallback(Throwable e)` (or parameterless void variant).","commonSituations":"Writing fallbacks for Completable-returning commands by analogy with void-returning commands; code-generation tools defaulting fallbacks to void; refactoring Observable commands to Completable while keeping void fallbacks.","solutions":["Change the fallback return type from void to Completable (or another wrappable type such as Observable/Single)","In the Completable fallback, signal completion via Completable.complete() or an error via Completable.error(e) instead of relying on void return","Keep a naming/convention check in code review for Completable commands' fallbacks","Add a CI test that triggers the fallback path once so the definition is validated"],"exampleFix":"// before\n@HystrixCommand\npublic Completable doWork() { ... }\n\nprivate void doWorkFallback(Throwable e) { log(e); }\n\n// after\n@HystrixCommand\npublic Completable doWork() { ... }\n\nprivate Completable doWorkFallback(Throwable e) { log(e); return Completable.complete(); }","handlingStrategy":"validation","validationCode":"Class<?> cmdRet = commandMethod.getReturnType();\nif (Completable.class.isAssignableFrom(cmdRet) && fallbackMethod.getReturnType() == Void.TYPE) {\n    throw new IllegalStateException(\"Completable command requires non-void fallback: \" + fallbackMethod);\n}","typeGuard":null,"tryCatchPattern":"catch (FallbackDefinitionException e) { log.error(\"Completable command has void fallback: {}\", e.getMessage()); failBuild(e); }","preventionTips":["For Completable commands, make fallbacks return Completable.complete()/error(e)","Add code-review convention: no void fallbacks anywhere in Javanica code","Cover the fallback path in tests, not just the happy path"],"tags":["hystrix","javanica","fallback","completable","rxjava","validation"],"backgroundTag":null,"analyzedSha":"5ce3bc58c38e7ca60ef2fe0e516e390e294ad941","analyzedAt":"2026-08-14T10:55:35.600Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}