Skip to content

PropertyBatchUpdateException: causes of nested PropertyAccessExceptions not shown in output #34691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rursprung opened this issue Mar 31, 2025 · 2 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@rursprung
Copy link

when a PropertyBatchUpdateException gets instantiated it does not pass the PropertyAccessExceptions as cause to its super (BeansException), instead storing it locally in propertyAccessExceptions:

/**
* Create a new PropertyBatchUpdateException.
* @param propertyAccessExceptions the List of PropertyAccessExceptions
*/
public PropertyBatchUpdateException(PropertyAccessException[] propertyAccessExceptions) {
super(null, null);
Assert.notEmpty(propertyAccessExceptions, "At least 1 PropertyAccessException required");
this.propertyAccessExceptions = propertyAccessExceptions;
}

this makes sense since propertyAccessExceptions is a list while cause can only be a single exception.
however, the output of this is really nondescript and does not contain the actual root cause:

Error creating bean with name 'exampleRepository' defined in org.example.repository.exampleRepository defined in @EnableElasticsearchRepositories declared on OpenSearchConfiguration: Failed properties: Property 'elasticsearchOperations' threw exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exampleRepository' defined in org.example.repository.exampleRepository defined in @EnableElasticsearchRepositories declared on OpenSearchConfiguration: Failed properties: Property 'elasticsearchOperations' threw exception
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1746)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1460)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
	at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:346)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1149)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1121)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1056)
	at app//org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
	at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
	at app//org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
	at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
	at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
	at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at app//org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1461)
	at app//org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
	at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
	at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
	at app//org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
	at app//org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener.injectFields(BeanOverrideTestExecutionListener.java:93)
	at app//org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener.prepareTestInstance(BeanOverrideTestExecutionListener.java:64)
	at app//org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260)
	at app//org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:160)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base@21.0.5/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base@21.0.5/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base@21.0.5/java.util.Optional.orElseGet(Optional.java:364)
	at java.base@21.0.5/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'elasticsearchOperations' threw exception
	at app//org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:135)
	at app//org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:79)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1743)
	... 43 more

as you can see it includes PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'elasticsearchOperations' threw exception which comes from its name:

super(propertyChangeEvent, "Property '" + propertyChangeEvent.getPropertyName() + "' threw exception", cause);

however it doesn't say what that exception (i.e. its cause) is. it has a cause set (i checked with the debugger), it's just a matter of also printing it. i presume the formatting for printing is done by PropertyBatchUpdateException#toString:

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName()).append("; nested PropertyAccessExceptions (");
sb.append(getExceptionCount()).append(") are:");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
sb.append('\n').append("PropertyAccessException ").append(i + 1).append(": ");
sb.append(this.propertyAccessExceptions[i]);
}
return sb.toString();
}

it seems that the standard PropertyAccessException#toString does not include the cause, so maybe that needs to be handled by PropertyBatchUpdateException#toString?

(i didn't create a minimum repro for this as i think the affected code is quite obvious from the callstacks & source links provided)

@rursprung rursprung changed the title PropertyBatchUpdateException: causes not shown in output PropertyBatchUpdateException: causes of nested PropertyAccessExceptions not shown in output Mar 31, 2025
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 31, 2025
@jhoeller jhoeller added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 1, 2025
@jhoeller jhoeller self-assigned this Apr 1, 2025
@jhoeller jhoeller added this to the 6.2.6 milestone Apr 1, 2025
@jhoeller jhoeller added the for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x label Apr 1, 2025
@github-actions github-actions bot added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-6.1.x Marks an issue as a candidate for backport to 6.1.x labels Apr 1, 2025
jhoeller added a commit that referenced this issue Apr 1, 2025
@rursprung
Copy link
Author

that was super fast - thanks @jhoeller!

@jhoeller
Copy link
Contributor

jhoeller commented Apr 2, 2025

Thanks for reporting it! This turned out to be a bug in MethodInvocationException after all: Ever since our change of cause handling in the NestedRuntimeException base class, MethodInvocationException effectively did not include the actual cause in its exposed message anymore. I've revised this for 6.2.6 and 6.1.19.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants