Activity exception is wrapped as ApplicationFailure in the workflow

Hi! We have a simple workflow with an activity

public class MyException extends RuntimeException {
  private Integer data; 
  public MyException(Integer data) { this.data = data; }
  public Integer getData() { return data; }
}

public class MyActivityImpl implements MyActivity {
  @Override
  public void doSomething() {
    if (someSpecificCondition) throw new MyException(1234);
    ...
  }
}

That is called from the workflow

@WorkflowInterface
public interface MyWorkflow {
    @WorkflowMethod
    void myMethod();
}

public class MyWorkflowImpl implements MyWorkflow {

  private final MyActivity myActivity;
  ...
  @Override
  public void myMethod() {
    try {
      myActivity.doSomething();
    }
    catch (TemporalException e) {
      // Handle exception when retries are exhausted
      // using ((MyException)...).getData()
    }
  }
}

And after reading this other thread ( Handling Exceptions from Activities with a RetryPolicy ) we are correctly navigating through the causes to find the underlying exceptions.

We are now needing to retrieve the information passed to the exception, but at the catch every exception in the stack is an ApplicationFailure containing the exception type and the message, but not the data inside the exception. So, we cannot call getData to retrieve the specific information.

Is there any way to propagate the custom exception, or include it as part of the ApplicationFailure? Should we completely reconsider that approach?

Thank you,
Pablo.

In your Activity you should not throw checked exceptions. If you need to propagate a checked exception, use Activity.wrap, for example:

  if (someSpecificCondition) {
    throw Activity.wrap(new MyException("This is my custom activity exception message"));
 }

Then in your workflow you can get this message out with for example:

try {
   return activities. doSomething();
} catch (ActivityFailure e) {
   logger.error(((ApplicationFailure) e.getCause()).getOriginalMessage());
   ...
}
1 Like

Thanks for the answer,

The exception we are throwing is unchecked, so I believe there is no need to wrap it. The issue is we need that custom data inside the exception, which is not a string message, to decide how we want to handle it.

I’ve updated the original message a bit to make it clearer.

To pass a custom data through an ApplicationFailure from the activity code explicitly. The ApplicationFailure factory method accepts the details arguments that can be accessed through ApplicationFailure.getDetails.

Hi,

I am trying to throw a custom exception(i.e ApplicationException internally extending RuntimeException) and the same is wrapped using Activity.wrap(). But when the same is caught in Workflow code as part of catch block, I am unable to get the details of my custom exception. Can you please suggest on the same.

Activity code :

try {
      return activity.doSomething();
    } catch (ApplicationException e) {
      throw Activity.wrap(e);
    }

Workflow code is as below :

try{
activityA.call();
}catch(ActivityFailure af) {
if ((((ApplicationFailure) af.getCause())
          .getType()
          .equals(ApplicationException.class.getName()))) {
        Values values = ((ApplicationFailure) af.getCause()).getDetails();
      }

Values is empty in above code. Need details about how to fetch the details that were wrapped inside ApplicationException class.

@poojabhutada
I tested by creating a custom “ApplicationException” in in activity code doing:

throw Activity.wrap(new ApplicationException("simulated"));

and can get the “simulated” message in workflow code via

} catch (ActivityFailure failure) {
     String message = ((ApplicationFailure) failure.getCause()).getOriginalMessage(); // should be "simulated" String
}

Is this what you are looking for?

@tihomir I am looking for custom fields populated as part of the exception. I have a list of ValidationErrors populated as part of the custom exception(i.e BusinessException class), so trying to read the same in catch block.

Use ApplicaitonException directly. It supports “details” that can be any object that is supported by DataConverter.

@maxim Do you mean as below :

catch (ApplicationFailure af) {
      Values values = af.getDetails();
      }

I think (could be wrong) what’s meant is to throw ApplicationFailure from your activity code as you can define custom details, for example:

throw ApplicationFailure.newFailure("My Custom Failure...", "MyCustomFailure",
                new Customer("John"), new Customer("Marry"));

where “MyCustomFailure” is the failure type and the two customer objects are details that you want to be able retrieve in your workflow code where you catch the failure, for example:

} catch (ActivityFailure failure) {
   ApplicationFailure af = (ApplicationFailure) failure.getCause();
   Customer c1 = af.getDetails().get(0, Customer.class);
   Customer c2 = af.getDetails().get(1, Customer.class);
   // ...
}

Thanks @tihomir . Will try out this approach. But will this approach have any impact on the exception types that we define as doNotRetry in activity retry options?

@poojabhutada

But will this approach have any impact on the exception types that we define as doNotRetry in activity retry options?

No it will not, for the previous example:

throw ApplicationFailure.newFailure("My Custom Failure...", "MyCustomFailure",
                new Customer("John"), new Customer("Marry"));

you can do:

setDoNotRetry("MyCustomFailure")

In your ActivityOptions->RetryOptions and it will be treated as a non-retryable failure

1 Like