{"record":{"id":"ef17dca7b4277d4d","repo":"apache/dubbo","slug":"method-config-error-return-attribute-must-be-set","errorCode":null,"errorMessage":"method config error : return attribute must be set true when on-return or on-throw has been set.","messagePattern":"method config error : return attribute must be set true when on-return or on-throw has been set\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java","lineNumber":279,"sourceCode":"                        }\n                    } catch (Exception e) {\n                        logger.info(\"Failed to override the property \" + method.getName() + \" in \"\n                                + this.getClass().getSimpleName()\n                                + \", please make sure every property has getter/setter method provided.\");\n                    }\n                }\n            }\n        }\n    }\n\n    public AsyncMethodInfo convertMethodConfig2AsyncInfo() {\n        if ((getOninvoke() == null && getOnreturn() == null && getOnthrow() == null)) {\n            return null;\n        }\n\n        // check config conflict\n        if (Boolean.FALSE.equals(isReturn()) && (getOnreturn() != null || getOnthrow() != null)) {\n            throw new IllegalStateException(\n                    \"method config error : return attribute must be set true when on-return or on-throw has been set.\");\n        }\n\n        AsyncMethodInfo asyncMethodInfo = new AsyncMethodInfo();\n\n        asyncMethodInfo.setOninvokeInstance(getOninvoke());\n        asyncMethodInfo.setOnreturnInstance(getOnreturn());\n        asyncMethodInfo.setOnthrowInstance(getOnthrow());\n\n        try {\n            if (StringUtils.isNotEmpty(oninvokeMethod)) {\n                asyncMethodInfo.setOninvokeMethod(getMethodByName(getOninvoke().getClass(), oninvokeMethod));\n            }\n\n            if (StringUtils.isNotEmpty(onreturnMethod)) {\n                asyncMethodInfo.setOnreturnMethod(getMethodByName(getOnreturn().getClass(), onreturnMethod));\n            }\n","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/config/MethodConfig.java#L261-L297","documentation":"Thrown by MethodConfig.convertMethodConfig2AsyncInfo() when an async callback (onreturn or onthrow) is configured but the method's 'return' attribute is set to false. The on-return and on-throw callbacks only make sense when the invocation actually returns a value or throws, so Dubbo rejects the contradictory config. This check fires during conversion of the method config into an AsyncMethodInfo.","triggerScenarios":"Configuring a method with <dubbo:method name=\"...\" return=\"false\" onreturn=\"...\"/> or onthrow while return is false. Programmatic equivalent: methodConfig.setReturn(false) together with setOnreturn(...)/setOnthrow(...).","commonSituations":"Enabling fire-and-forget (return=false) for performance but leaving an on-return callback attached from a prior copy-paste config. Migrating a sync method to async and forgetting to update return.","solutions":["Set return=\"true\" (the default) when onreturn or onthrow callbacks are specified.","Remove the onreturn/onthrow callbacks if the method is genuinely fire-and-forget (return=false).","Review the method config block and ensure callback attributes and return are consistent."],"exampleFix":"// before\n<dubbo:method name=\"doWork\" return=\"false\" onreturn=\"callback\"/>\n// after\n<dubbo:method name=\"doWork\" return=\"true\" onreturn=\"callback\"/>","handlingStrategy":"validation","validationCode":"boolean hasCallback = methodConfig.getOnreturn() != null || methodConfig.getOnthrow() != null;\nboolean returnDisabled = Boolean.FALSE.equals(methodConfig.isReturn());\nif (hasCallback && returnDisabled) {\n    throw new IllegalStateException(\"onreturn/onthrow require return=true\");\n}\nmethodConfig.convertMethodConfig2AsyncInfo();","typeGuard":"static boolean isAsyncConfigConsistent(MethodConfig m) {\n    boolean hasCb = m.getOnreturn() != null || m.getOnthrow() != null;\n    boolean retDisabled = Boolean.FALSE.equals(m.isReturn());\n    return !(hasCb && retDisabled);\n}","tryCatchPattern":"try {\n    asyncInfo = methodConfig.convertMethodConfig2AsyncInfo();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"return attribute must be set true\")) {\n        methodConfig.setReturn(true);\n        asyncInfo = methodConfig.convertMethodConfig2AsyncInfo();\n    } else throw e;\n}","preventionTips":["When adding onreturn/onthrow, ensure return is not explicitly false.","For fire-and-forget methods, remove all callbacks.","Add a config-consistency assertion in test setup."],"tags":["config","method","async-callback","validation"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}