This is a follow-up of issue https://github.com/spring-projects/spring-boot/issues/41492.

We have now deployed Spring Boot 3.3.2 in the affected environment and the original problem addressed in the issue above still exists even though the code change is in place: We get the NPE "because "firstHalf" is null" and the process exits but the original exception is not logged.

When running with -XX:ActiveProcessorCount=1 I have found an InterruptedException from kafka while processing some event. The stack trace does not fit, but I take the idea: What if an InterruptedException is thrown ThreadedOutcomesResolver.resolveOutcomes()? Then line 166 is hit and still a NPE can occur because this.outcomes is not initialized.

https://github.com/spring-projects/spring-boot/blob/c7e29b7b1bc570a1862a1aeacba5dd390bf3b5c6/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java#L166

best regards Jens

Comment From: snicoll

@claussenj I am surprised the original fix did not work. Is there a way for us to reproduce the issue? Can you share a small sample with instructions?

Comment From: claussenj

Hi Stephane @snicoll, I have tried to reproduce and construct a local example, but without success. Even more strange, today the issue was not even reproducible in the affected environment. Not sure what is going on. Let's close this issue for now. If it comes back I'll open a new one after my vacation. regards Jens

Comment From: snicoll

Thanks for following-up.

If it comes back I'll open a new one after my vacation.

Please don't, and rather add a comment here and we can reopen this one.

Comment From: claussenj

Hello Team, finally the issue has indeed come back and we have made good progress analyzing it. Let me know if we can reopen this one or should I open a new one?

  • Spring Boot Version 3.3.4

Here are the details: 1. A -javaagent calls Thread.currentThread().interrupt() on the main thread. You may argue that this should not happen, and indeed we are chasing this aspect of the problem as well. 2. The resulting InterruptedException is handled here: https://github.com/spring-projects/spring-boot/blob/32af30495e4de23af346e94663ec3427181cfe48/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java#L166 3. When the InterruptedException is caught then ThreadedOutcomesResolver.outcomes is null 4. When ThreadedOutcomesResults.outcomes is null then firstHalf is null here

As a temp workaround we set ThreadedOutcomesResolver.outcomes to an empty array when the InterruptedException is caught. Maythe the same should also take place when an exception is caught in the thread

Here are the exceptions:

java.lang.InterruptedException
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Thread.join(Thread.java:1313)
at java.base/java.lang.Thread.join(Thread.java:1381)
at org.springframework.boot.autoconfigure.condition.OnClassCondition$ThreadedOutcomesResolver.resolveOutcomes(OnClassCondition.java:169)
at org.springframework.boot.autoconfigure.condition.OnClassCondition.resolveOutcomesThreaded(OnClassCondition.java:70)
at org.springframework.boot.autoconfigure.condition.OnClassCondition.getOutcomes(OnClassCondition.java:53)
at org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition.match(FilteringSpringBootCondition.java:49)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$ConfigurationClassFilter.filter(AutoConfigurationImportSelector.java:366)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:131)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:813)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:743)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:714)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:183)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:417)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
at com.sap.crun.perfmon.Application.main(Application.java:39)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:102)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:64)
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:40)

Exception finally leading to crash (same as before):
"java.lang.NullPointerException: Cannot read the array length because \"firstHalf\" is null"
"\tat org.springframework.boot.autoconfigure.condition.OnClassCondition.resolveOutcomesThreaded(OnClassCondition.java:73)"
"\tat org.springframework.boot.autoconfigure.condition.OnClassCondition.getOutcomes(OnClassCondition.java:53)"
"\tat org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition.match(FilteringSpringBootCondition.java:49)"
"\tat org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$ConfigurationClassFilter.filter(AutoConfigurationImportSelector.java:366)"
"\tat org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:131)"
"\tat org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)"
"\tat org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:813)"
"\tat org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:743)"
"\tat org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:714)"
"\tat org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:183)"
"\tat org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:417)"
"\tat org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290)"
"\tat org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)"
"\tat org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)"
"\tat org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:789)"
"\tat org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:607)"
"\tat org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)"
"\tat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)"
"\tat org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)"
"\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:335)"
"\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)"
"\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)"
"\tat com.sap.crun.perfmon.Application.main(Application.java:39)"
"\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)"
"\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)"
"\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)"
"\tat java.base/java.lang.reflect.Method.invoke(Method.java:569)"
"\tat org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:102)"
"\tat org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:64)"
"\tat org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:40)"]}

Let me know if any other details are needed.

regards Jens