Handling Exceptions from Activities with a RetryPolicy

Is it possible to handle a RuntimeException thrown from an Activity in a Workflow when the RuntimeException is set in the Activity RetryPolicy as DoNotRetry? I’m attempting to use the same method described in Handle activity failure exceptions in workflows but this doesn’t appear to be working as I would expect. When catching Throwable in the Workflow the instance caught is an instance of ActivityFailiure, not the RuntimeException thrown from the Activity. I’m not sure if the RetryPolicy has anything to do with the behaviour I’m seeing, but just thought I’d mention it. Further, I’m using Kotlin which does some “magic” with checked and unchecked exceptions which might be at play here. I tried both throwing the Exception from the Activity (which because it’s Kotlin is always a RuntimeException) and using Activity.wrap–both have the same behaviour described above.

1 Like

Any unknown runtime exception that is thrown from an activity is converted to ApplicationFailure. The rules of conversion are the following:

  • type is set to the full exception class name. This value should be used by DoNotRetry field.
  • message is set to the exception message
  • trace is set to the exception stack trace
  • nonRetryable is set to false
  • chained exception is converted as well and chained to the ApplicationFailure.

If an activity fails with a runtime exception the workflow always receives ActivityFailure from an activity invocation. With the ApplicationFailure exception thrown from the activity attached to it as a cause.

If an activity fails with a runtime exception the workflow always receives ActivityFailure from an activity invocation.

Should I be throwing a checked exception from the Activity so the Workflow can catch it in a standard try-catch block? The Workflow is unable to handle unchecked exceptions thrown from an Activity?

After doing some further reading I’m starting to think I’ll need to throw an ApplicationFailure via newNonRetryableFailure from the Activity in order to handle a custom expectation in the Workflow. I was expecting to be able to do something like the following (not even sure if this compiles)

// from the Workflow
try {
   activities.throwAnException()
} catch (MyCustomException e) {
  // do something
}
// from the activity
class MyActivityImpl : MyActivity {
  @Override
  public throwAnException() {
    throw ApplicationFailure.newNonRetryableFailure("This failed", MyCustomException.getClass().getName())
  }
}

I tried this as well with no such success. I can see I’m getting an ApplicationFailure whose type is set to the fully qualified class name of the custom exception. Do I need to inspect the type of the ApplicationFailure to build my application logic in the Workflow?

I took a look at this example and don’t see an answer to handling a custom exception in a Workflow thrown from an Activity. Maybe I’m missing this in the documentation somewhere else?

You are correct that checking the type of the ApplicationFailure is the way for a workflow to discriminate the failures.

This worked as expected. I’m able to catch the ActivityFailure, get the cause as an ApplicationFailure, and then compare the type value to the fully qualified name of the exception I want to handle.

try {
  activities.throwsAnException()
} catch (e: ActivityFailure) {
  val type = (e.cause as? ApplicationFailure)?.type
  when (type) {
    MyCustomException::class.qualifiedName -> {
      // handle the custom exception
    } else -> throw e
  }
}

Would it be worth adding an example in the temporal samples? I couldn’t find how to handle user-defined exceptions in a Workflow which are thrown from an Activity in the samples nor in the documentation (but maybe I missed it?) I can contribute a sample if that’s helpful.

Great that it worked!

We are in the process of rewriting our SDK documentation and samples. The error handling is certainly will be covered in the new docs.