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 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.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java:435
/**
* Process a return value. Wraps a return of {@code this} if necessary to be the
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
* Also takes care of the conversion from {@code Mono} to Kotlin Coroutines if needed.
*/
private static @Nullable Object processReturnType(
Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Object returnValue) {
// Massage return value if necessary
if (returnValue != null && returnValue == target &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this". Note that we can't help
// if the target sets a reference to itself in another returned object.
returnValue = proxy;
}
Class<?> returnType = method.getReturnType();
if (returnValue == 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(returnValue) :
CoroutinesUtils.awaitSingleOrNull(returnValue, arguments[arguments.length - 1]);
}
return returnValue;
}
/**
* Serializable replacement for CGLIB's NoOp interface.
* Public to allow use elsewhere in the framework.
*/
public static class SerializableNoOp implements NoOp, Serializable {
}
View on GitHub (pinned to e8729d0438)
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.
Example fix
// before
@Around("execution(int *.count(..))")
public Object around(ProceedingJoinPoint pjp) {
if (cacheMiss) return null; // throws for int return
return pjp.proceed();
}
// after
@Around("execution(int *.count(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
if (cacheMiss) return 0; // primitive default
return pjp.proceed();
} Defensive patterns
Strategy: validation
Validate before calling
Object result = ...; // from advice or proceed()
Class<?> rt = method.getReturnType();
if (result == null && rt != void.class && rt.isPrimitive()) {
result = defaultPrimitiveValue(rt); // 0, false, 0.0
} Type guard
static boolean isPrimitiveReturn(Method m) {
Class<?> rt = m.getReturnType();
return rt != void.class && rt.isPrimitive();
}
static Object defaultPrimitiveValue(Class<?> primitive) {
if (primitive == boolean.class) return false;
if (primitive == long.class) return 0L;
if (primitive == float.class) return 0f;
if (primitive == double.class) return 0d;
if (primitive == char.class) return '\0';
return 0; // int, short, byte
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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/09a72c997efdd5a0.json.
Report an issue: GitHub.