spring-projects/spring-framework · error · AopInvocationException
Null return value from advice does not match primitive retur
Error message
Null return value from advice does not match primitive return type for: {method} What it means
Thrown by JdkDynamicAopProxy.invoke for the JDK-proxy counterpart of error 76: after the interceptor chain proceeded, the return value is null but the method's declared return type is a primitive (and not void). Because a JDK proxy cannot assign null to a primitive return, Spring throws AopInvocationException with a descriptive message rather than letting a generic NullPointerException leak through the generated InvocationHandler. This is the JDK-proxy mirror of the CGLIB processReturnType check.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java:236
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != void.class && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
if (COROUTINES_REACTOR_PRESENT && KotlinDetector.isSuspendingFunction(method)) {
return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ?
CoroutinesUtils.asFlow(retVal) : CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}View on GitHub (pinned to e8729d0438)
Solutions
- Never return null from advice for primitive-returning methods; return the type's default value (0 for numeric, false for boolean).
- Guard with method.getReturnType().isPrimitive() inside the advice and substitute the appropriate default.
- Ensure proceed() is invoked and its result returned when the advice should delegate.
Example fix
// before
@Around("execution(boolean *.isActive(..))")
public Object around(ProceedingJoinPoint pjp) {
if (featureDisabled) return null; // throws: primitive boolean
return pjp.proceed();
}
// after
@Around("execution(boolean *.isActive(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
if (featureDisabled) return false;
return pjp.proceed();
} Defensive patterns
Strategy: validation
Validate before calling
Object result = ...;
Class<?> rt = method.getReturnType();
if (result == null && rt != void.class && rt.isPrimitive()) {
result = defaultPrimitiveValue(rt);
}
static Object defaultPrimitiveValue(Class<?> p) {
if (p == boolean.class) return false;
if (p == long.class) return 0L;
if (p == float.class) return 0f;
if (p == double.class) return 0d;
if (p == char.class) return '\0';
return 0;
} Type guard
static boolean isPrimitiveReturn(Method m) {
Class<?> rt = m.getReturnType();
return rt != void.class && rt.isPrimitive();
} Prevention
- Return a primitive default (0/false) instead of null from advice on primitive-returning interface methods.
- Detect primitive return types in the advice and branch.
- Unit-test advice against boolean/int-returning interface methods.
When it happens
Trigger: An Around advice returning null for a primitive-returning method on an interface-proxied bean (proxyTargetClass=false or interface-only target); the advice chain short-circuits and returns null; a MethodInterceptor returns null where the interface declares a primitive return.
Common situations: Writing @Around caching/null-handling advice for interface methods returning boolean/int/long; testing mocks that return null for primitive returns; switching from CGLIB to JDK proxy and the same null-return bug surfaces on the JDK path.
Related errors
- Null return value from advice does not match primitive retur
- Found {} unbound primitive arguments with no way to distingu
- Found {} candidate variable names but only one candidate bin
- Advice must be declared inside an aspect type: Offending met
- Unsupported advice type on method: {}
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/fe6ef4106173d23f.json.
Report an issue: GitHub.