spring-projects/spring-framework · error · AopInvocationException

Null return value from advice does not match primitive…

Error message

Null return value from advice does not match primitive return type for: {}

What it means

CglibAopProxy.processReturnType() (line 423) inspects each invocation's return. If the target method declares a primitive return type (e.g. int) but the advice chain yields null (an Around advice returned null, or the target returned null via a boxed path), Spring throws AopInvocationException at line 435 because unboxing null to a primitive is impossible and would otherwise produce a confusing NPE.

Solutions

  1. In the @Around advice, never return null when the proxied method returns a primitive - return a proper default (0, false, etc.) or the result of proceed()
  2. Ensure proceeding.joinPoint.proceed() is always invoked and its result returned for primitive-returning methods
  3. Unit-test the advice against a primitive-returning method to catch null returns early

Example fix

// before
@Around("...")
public Object cache(ProceedingJoinPoint pjp) {
  if (cacheMiss) return null;  // method returns int -> boom
  return pjp.proceed();
}

// after
@Around("...")
public Object cache(ProceedingJoinPoint pjp) throws Throwable {
  if (cacheMiss) return 0;  // match primitive return type
  return pjp.proceed();
}
Defensive patterns

Strategy: validation

Validate before calling

// Inside @Around, guard null returns for primitive methods:
Object result = pjp.proceed();
if (result == null && pjp.getSignature() instanceof MethodSignature ms
    && ms.getReturnType().isPrimitive()) {
  return Defaults.defaultValue(ms.getReturnType());
}
return result;

Type guard

private static boolean returnsPrimitive(Method m) {
  return m != null && m.getReturnType().isPrimitive();
}

Prevention

When it happens

Trigger: An @Around (or other advice that controls the return) returns null for a joinpoint whose method has a primitive return type; the target method returns null through a proxy path that loses autoboxing.

Common situations: An @Around advice that returns null on some branch (e.g. cache miss returning null, or short-circuit) for a method declared to return int/boolean/long; caching/aspect that forgets to call proceed() and returns null.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/b2840068ed749344. Report an issue: GitHub.

Appendix: 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 69bf83ad71)