{"id":"09a72c997efdd5a0","repo":"spring-projects/spring-framework","slug":"null-return-value-from-advice-does-not-match-primi","errorCode":null,"errorMessage":"Null return value from advice does not match primitive return type for: {method}","messagePattern":"Null return value from advice does not match primitive return type for: (.+?)","errorType":"exception","errorClass":"AopInvocationException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java","lineNumber":435,"sourceCode":"\n\t/**\n\t * Process a return value. Wraps a return of {@code this} if necessary to be the\n\t * {@code proxy} and also verifies that {@code null} is not returned as a primitive.\n\t * Also takes care of the conversion from {@code Mono} to Kotlin Coroutines if needed.\n\t */\n\tprivate static @Nullable Object processReturnType(\n\t\t\tObject proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Object returnValue) {\n\n\t\t// Massage return value if necessary\n\t\tif (returnValue != null && returnValue == target &&\n\t\t\t\t!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {\n\t\t\t// Special case: it returned \"this\". Note that we can't help\n\t\t\t// if the target sets a reference to itself in another returned object.\n\t\t\treturnValue = proxy;\n\t\t}\n\t\tClass<?> returnType = method.getReturnType();\n\t\tif (returnValue == null && returnType != void.class && returnType.isPrimitive()) {\n\t\t\tthrow new AopInvocationException(\n\t\t\t\t\t\"Null return value from advice does not match primitive return type for: \" + method);\n\t\t}\n\t\tif (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {\n\t\t\treturn COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?\n\t\t\t\t\tCoroutinesUtils.asFlow(returnValue) :\n\t\t\t\t\tCoroutinesUtils.awaitSingleOrNull(returnValue, arguments[arguments.length - 1]);\n\t\t}\n\t\treturn returnValue;\n\t}\n\n\n\t/**\n\t * Serializable replacement for CGLIB's NoOp interface.\n\t * Public to allow use elsewhere in the framework.\n\t */\n\tpublic static class SerializableNoOp implements NoOp, Serializable {\n\t}\n","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java#L417-L453","documentation":"Thrown by CglibAopProxy.processReturnType when an advice chain (or the target) returned null for a method whose declared return type is a primitive (int, boolean, etc.). Java cannot unbox null to a primitive, so rather than letting an automatic NullPointerException escape from an opaque generated subclass, Spring raises a descriptive AopInvocationException. void methods are exempt.","triggerScenarios":"An Around advice/interceptor that returns null for a method declared to return int/long/boolean/etc.; the target method returns null (only possible if bytecode was manipulated or via unusual reflection); a @Around advice that forgets to call proceed() or returns null deliberately.","commonSituations":"Writing a @Around aspect that does 'return null;' on a short-circuit path for a primitive-returning method; caching advice returning null on a miss for a primitive getter; mocking/testing advice that doesn't preserve return-type semantics.","solutions":["In the @Around advice, never return null for primitive-returning methods; return the primitive default (0, false, 0.0) or the result of proceeding().","If short-circuiting, detect the primitive return type via pjp.getMethod().getReturnType().isPrimitive() and return the appropriate default (e.g. 0/false).","Ensure the advice actually invokes proceeding once when it should delegate."],"exampleFix":"// before\n@Around(\"execution(int *.count(..))\")\npublic Object around(ProceedingJoinPoint pjp) {\n  if (cacheMiss) return null;  // throws for int return\n  return pjp.proceed();\n}\n\n// after\n@Around(\"execution(int *.count(..))\")\npublic Object around(ProceedingJoinPoint pjp) throws Throwable {\n  if (cacheMiss) return 0;  // primitive default\n  return pjp.proceed();\n}","handlingStrategy":"validation","validationCode":"Object result = ...; // from advice or proceed()\nClass<?> rt = method.getReturnType();\nif (result == null && rt != void.class && rt.isPrimitive()) {\n  result = defaultPrimitiveValue(rt); // 0, false, 0.0\n}","typeGuard":"static boolean isPrimitiveReturn(Method m) {\n  Class<?> rt = m.getReturnType();\n  return rt != void.class && rt.isPrimitive();\n}\nstatic Object defaultPrimitiveValue(Class<?> primitive) {\n  if (primitive == boolean.class) return false;\n  if (primitive == long.class)    return 0L;\n  if (primitive == float.class)   return 0f;\n  if (primitive == double.class)  return 0d;\n  if (primitive == char.class)    return '\\0';\n  return 0; // int, short, byte\n}","tryCatchPattern":null,"preventionTips":["Never return null from advice wrapping primitive-returning methods; return the primitive default.","Detect primitive return types inside advice and branch accordingly.","Add unit tests that exercise advice on boolean/int-returning methods."],"tags":["spring-aop","cglib","advice","primitive","return-value"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}