Error unwrapping

One of my activities makes a network call to an external gRPC service which returns normal grpc response;

Activity

I’m handing the error in the activity like so;

if err != nil {
	st := status.Convert(err)
	switch st.Code() {
	case codes.Unauthenticated:
		return nil, temporal.NewNonRetryableApplicationError(st.Message(), "Unauthenticated", st.Err())
	case codes.InvalidArgument:
		return nil, temporal.NewNonRetryableApplicationError(st.Message(), "InvalidArgument", st.Err(), util.ExpandErrorDetails(st.Details()))
	default:
		return nil, err
	}
}

Workflow

In the workflow I then do;

err = activityFuture.Get(ctx, &something)
if err != nil {
    var applicationErr *temporal.ApplicationError
    if errors.As(err, &applicationErr) {
        if applicationErr.NonRetryable() {
            var detailMsg interface{}
            _ = applicationErr.Details(&detailMsg)

            return nil, temporal.NewNonRetryableApplicationError(applicationErr.Error(), applicationErr.Type(), applicationErr.Unwrap(), detailMsg)
         }
    }

    return nil, err
}

Main Execution

Regardless of how many times I unwrap the error it still remains a temporal error

err = workflow.Get(ctx, &something)
if err != nil {
	var applicationErr *temporal.ApplicationError

	if errors.As(err, &applicationErr) {

    // I want to access the original grpc Error

    }
}

This is what I get on the client side - rpc error: code = NotFound desc = rpc error: code = NotFound desc = User not found (type: Error, retryable: true)

The first rpc error: code is the one the main execution returns
The second rpc error: code is from the activity

You will never get original error back here. Errors are serialized and transmitted between SDK and server during workflow execution. And it is not possible in go to build instance of concrete type dynamically. This is why we have workflow/activity registration which is technically a map between string and concrete type/func. We decided not to follow with the same approach for errors (registering every possible error type is overkill). Instead of this, all go errors are converted to ApplicationError and type of original error can be retrieved with Type() and message with standard Error() call.

Btw, you don’t need to unwrap error in workflow code. Just return it as is.