Describe the bug
When a custom Spring bean method (invoked via ${beanName.method(...)} in a DMN expression) throws an exception, Flowable's DMN engine catches it and wraps it in a FlowableException. However, the original exception is not preserved in the cause chain.
For example, our bean throws:
throw new CustomBadDMNArgumentsException("Threshold value cannot be null.");
What we receive in our custom BaseExceptionHandlerAdvice > @ExceptionHandler:
FlowableException: "DMN decision with key decision-xxx execution failed. Cause: Error while evaluating expression: ${collectionFunctions.numbersGreaterThan("all", number_list, 100)} with VariableContainerWrapper[...] in Execution[...]"
e.getCause() is null
- The original
CustomBadArgumentsException is completely lost
- Only the expression text appears in the message - not even the original exception's message
We confirmed this by logging the full cause chain in our exception handler - only depth 0 (FlowableException) was present with no nested causes.
This prevents us from:
- Differentiating between bad user input (400) and genuine server errors (500) in our REST API responses
- Returning meaningful, user-friendly error messages from DMN expression functions
- Using standard Spring
@ExceptionHandler patterns that rely on exception type matching or cause chain traversal
Expected behavior
The DMN expression evaluation layer should preserve the original exception in the cause chain when wrapping it. Similarly, the wrapping in the JUEL/EL evaluation layer and the InvocationTargetException unwrapping should ensure the original exception is carried through.
This would make custom DMN expression beans significantly more useful, since callers could inspect the cause chain to handle different error types appropriately.
Code
throw new CustomBadDMNArgumentsException("Threshold value cannot be null.");
We've implemented a ThreadLocal-based workaround where custom beans store the exception before throwing, and the exception handler recovers it. This works but is fragile and adds complexity that shouldn't be necessary.
Additional context
Flowable version: 3.17.13
Java version: 17
Component: flowable-dmn-engine, expression evaluation
Describe the bug
When a custom Spring bean method (invoked via ${beanName.method(...)} in a DMN expression) throws an exception, Flowable's DMN engine catches it and wraps it in a FlowableException. However, the original exception is not preserved in the cause chain.
For example, our bean throws:
throw new CustomBadDMNArgumentsException("Threshold value cannot be null.");What we receive in our custom
BaseExceptionHandlerAdvice>@ExceptionHandler:FlowableException: "DMN decision with key decision-xxx execution failed. Cause: Error while evaluating expression:${collectionFunctions.numbersGreaterThan("all", number_list, 100)} with VariableContainerWrapper[...] in Execution[...]"e.getCause()is nullCustomBadArgumentsExceptionis completely lostWe confirmed this by logging the full cause chain in our exception handler - only depth 0 (FlowableException) was present with no nested causes.
This prevents us from:
@ExceptionHandlerpatterns that rely on exception type matching or cause chain traversalExpected behavior
The DMN expression evaluation layer should preserve the original exception in the cause chain when wrapping it. Similarly, the wrapping in the JUEL/EL evaluation layer and the InvocationTargetException unwrapping should ensure the original exception is carried through.
This would make custom DMN expression beans significantly more useful, since callers could inspect the cause chain to handle different error types appropriately.
Code
throw new CustomBadDMNArgumentsException("Threshold value cannot be null.");We've implemented a
ThreadLocal-basedworkaround where custom beans store the exception before throwing, and the exception handler recovers it. This works but is fragile and adds complexity that shouldn't be necessary.Additional context
Flowable version: 3.17.13
Java version: 17
Component: flowable-dmn-engine, expression evaluation