Skip to content

DMN Expression Evaluation Loses Original Exception Cause Chain #4170

@bhawesh96

Description

@bhawesh96

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions