Hello, I am throwing Runtime Exceptions in my activities, which are non-retryable. I still get them wrapped in WorkflowFailedException, but lose internal details of the original thrown Runtime exception. I see ApplicationFailure has details, how can these be supplied so that I can use them when I catch and handle the WorkflowFailedExceptions?
How can I supply those details to the constructed ApplicationFailure? Right now, I am just throwing my custom RuntimeException from the Activity. I see the type being populated correctly in the cause, but see details being null.
Thanks for the answer, Maxim. A few more questions:
Even when I am throwing unchecked runtime exceptions from the activities, when I catch them in Workflow methods, I still see that they get wrapped inside an ActivityFailure, which has an ApplicationFailure as its cause, and this ApplicationFailure has its type as my thrown type. Is this expected?
When I unwrap the unchecked, runtime exception in my Workflow method and re-throw it, the hierarchy of the exception caught by the caller of the Workflow changes from WorkflowFailedException(cause=ActivityFailure(cause=ApplicationFailure(…)…)…) to WorkflowFailedException(cause=(ApplicationFailure(…))…). Is this expected? Is there some documentation which I can refer to, which has these rules of wrapping?
Yes, this is by design. The reason is that ActivityFailure contains all the relevant context information about the activity invocation. This way it is clear which exact activity failed. Just throwing ApplicationFailure from the activity invocation would lose all the context.
Any unknown exception is converted to the ApplicationFailure to avoid serializing/deserializing exceptions. This is done as not all exceptions in Java can be serialized as well as for interoperability with SDKs in other languages. For example, it would be impossible to deal with a serialized Java exception from the Go SDK. Another problem with serializing/deserializing exceptions is that the calling workflow process might not have class definitions for some exceptions thrown from activities.
The rules are:
Any exception that doesn’t extend TemporalFailure is converted to ApplicationFailure when thrown from a workflow or an activity.
The ApplicationFailure extends TemporalFailure. So it is passed to a caller without any conversion.
An activity invocation always throws ActivityFailure with an original failure as a cause.
A child workfow invocation always throws ChildWorkflowFailure with an original failure as a cause.
A synchronous workflow invocation always returns WorkflowException which will contain the workflow failure as a cause.