spring-projects/spring-framework · error · IllegalStateException
No executor specified and no default executor set on AsyncEx
Error message
No executor specified and no default executor set on AsyncExecutionInterceptor either
What it means
Thrown by AsyncExecutionInterceptor.invoke when determineAsyncExecutor returns null: no qualifier was set, no explicit executor was supplied, and getDefaultExecutor resolved to null. AsyncExecutionInterceptor normally falls back to a SimpleAsyncTaskExecutor, so reaching this branch means a custom subclass overrode getDefaultExecutor to return null, or the executor was explicitly cleared.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java:106
super(defaultExecutor, exceptionHandler);
}
/**
* Intercept the given method invocation, submit the actual calling of the method to
* the correct task executor and return immediately to the caller.
* @param invocation the method to intercept and make asynchronous
* @return {@link Future} if the original method returns {@code Future}; {@code null}
* otherwise.
*/
@Override
public @Nullable Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
final Method userMethod = BridgeMethodResolver.getMostSpecificMethod(invocation.getMethod(), targetClass);
AsyncTaskExecutor executor = determineAsyncExecutor(userMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
Callable<Object> task = () -> {
try {
Object result = invocation.proceed();
if (result instanceof Future<?> future) {
return future.get();
}
}
catch (ExecutionException ex) {
Throwable cause = ex.getCause();
handleError(cause == null ? ex : cause, userMethod, invocation.getArguments());
}
catch (Throwable ex) {
handleError(ex, userMethod, invocation.getArguments());
}
return null;View on GitHub (pinned to e8729d0438)
Solutions
- Define a TaskExecutor bean (optionally named 'taskExecutor') in the ApplicationContext so getDefaultExecutor resolves it.
- Pass a concrete Executor to the constructor or call setExecutor(executor) on the interceptor.
- If subclassing, keep AsyncExecutionInterceptor's SimpleAsyncTaskExecutor fallback rather than returning null from getDefaultExecutor.
Example fix
// before
@Override
protected Executor getDefaultExecutor(BeanFactory bf) {
return null; // fail-fast -> error 102
}
// after
@Override
protected Executor getDefaultExecutor(BeanFactory bf) {
Executor e = super.getDefaultExecutor(bf);
return (e != null ? e : new SimpleAsyncTaskExecutor());
} Defensive patterns
Strategy: validation
Validate before calling
Executor ex = interceptor.determineAsyncExecutor(method); // or resolve eagerly
if (ex == null) {
ex = new SimpleAsyncTaskExecutor();
interceptor.setExecutor(ex);
} Prevention
- Always define a TaskExecutor bean (named 'taskExecutor' if multiple exist).
- Pass a concrete Executor when constructing interceptors programmatically.
- Do not override getDefaultExecutor to return null without supplying an alternative.
When it happens
Trigger: A subclass of AsyncExecutionAspectSupport overrides getDefaultExecutor to return null (instead of falling back) and no executor was passed via constructor/setExecutor; or setExecutor(null) was called and the supplier yields null.
Common situations: Custom async aspect that tries to fail-fast when no TaskExecutor bean is present; programmatic AOP wiring that forgets to provide an Executor; disabling the SimpleAsyncTaskExecutor fallback.
Related errors
- BeanFactory must be set on {getClass().getSimpleName()} to a
- MethodInvocation is not a Spring ProxyMethodInvocation: {}
- 'argumentNames' property of AbstractAspectJAdvice contains a
- Only afterReturning advice can be used to bind a return valu
- Only afterThrowing advice can be used to bind a thrown excep
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/3768a31f8fa58bb2.json.
Report an issue: GitHub.