Since Spring Boot version 2.6.2, my app no longer starts due to a weird exception through when it is trying to instantiate an implementation of the functional interface TaskSchedulerCustomizer. I am using this customizer to set an error handler on the ThreadPoolTaskScheduler.

I am developing with kotlin 1.6.10 and have not had any issues with 2.6.1. I am also using Spring Cloud 2021.0.1

I have tried with Spring Boot 2.6.3 and 2.6.4 but same error.

Sample of my stacktrace

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskSchedulerCustomizer' defined in class path resource [<removed package name>/configs/TaskSchedulerConfigs.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.task.TaskSchedulerCustomizer]: Illegal arguments to factory method 'taskSchedulerCustomizer'; args: ; nested exception is java.lang.IllegalArgumentException: object is not an instance of declaring class
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)

Sample of my configuration class:

@Configuration
@EnableAsync
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
class TaskSchedulerConfigs(
    private val eventProcessor: EventProcessor,
    private val xfConfigs: XFConfigs
) : Logging, AsyncConfigurer {

    @Bean
    fun taskSchedulerCustomizer(): TaskSchedulerCustomizer {
        return TaskSchedulerCustomizer {
            taskScheduler: ThreadPoolTaskScheduler -> taskScheduler.setErrorHandler(TSErrorHandler())
        }
    }

    override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler? {
        return AsyncUncaughtExceptionHandler { ex, method, params ->
            loge(ex)
            eventProcessor.createSystem(
                subType = EventSubType.ALERT,
                details = "System task failure: ${ex.message}, method=$method, params=$params",
                principal = "TaskScheduler"
            )
        }
    }

    private inner class TSErrorHandler : ErrorHandler {
        override fun handleError(t: Throwable) {
            loge(t)
            eventProcessor.createSystem(
                subType = EventSubType.ALERT,
                details = "System task failure: ${t.message}",
                principal = "TaskScheduler"
            )
        }
    }
    [...]
}

Comment From: wilkinsona

I assume you're using Spring Cloud Sleuth in which case this is a duplicate of https://github.com/spring-projects/spring-boot/issues/29151. If that's not the case, please provide a complete yet minimal sample that reproduces the problem and we can take another look. That sample should be something that we can unzip or git clone and run to reproduce the failure.

Comment From: renaudberube

Yes, I am indeed using Spring Cloud Sleuth. Thanks for the quick response. I used the workaround detailed in #29151: I moved the implementation of AsyncConfigurer to another class annotated with @Configuration and it solved my problem.