Skip to content

Commit f9580aa

Browse files
Align Nexus handler failure conversion with other SDKs (#2613)
1 parent 26546a7 commit f9580aa

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

temporal-sdk/src/main/java/io/temporal/internal/nexus/NexusTaskHandlerImpl.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static io.temporal.internal.common.NexusUtil.nexusProtoLinkToLink;
55

66
import com.uber.m3.tally.Scope;
7+
import io.grpc.StatusRuntimeException;
78
import io.nexusrpc.Header;
89
import io.nexusrpc.OperationException;
910
import io.nexusrpc.handler.*;
@@ -12,6 +13,7 @@
1213
import io.temporal.api.nexus.v1.*;
1314
import io.temporal.client.WorkflowClient;
1415
import io.temporal.client.WorkflowException;
16+
import io.temporal.client.WorkflowNotFoundException;
1517
import io.temporal.common.converter.DataConverter;
1618
import io.temporal.common.interceptors.WorkerInterceptor;
1719
import io.temporal.failure.ApplicationFailure;
@@ -203,6 +205,9 @@ private CancelOperationResponse handleCancelledOperation(
203205
private void convertKnownFailures(Throwable e) {
204206
Throwable failure = CheckedExceptionWrapper.unwrap(e);
205207
if (failure instanceof WorkflowException) {
208+
if (failure instanceof WorkflowNotFoundException) {
209+
throw new HandlerException(HandlerException.ErrorType.NOT_FOUND, failure);
210+
}
206211
throw new HandlerException(HandlerException.ErrorType.BAD_REQUEST, failure);
207212
}
208213
if (failure instanceof ApplicationFailure) {
@@ -213,6 +218,10 @@ private void convertKnownFailures(Throwable e) {
213218
HandlerException.RetryBehavior.NON_RETRYABLE);
214219
}
215220
}
221+
if (failure instanceof StatusRuntimeException) {
222+
StatusRuntimeException statusRuntimeException = (StatusRuntimeException) failure;
223+
throw convertStatusRuntimeExceptionToHandlerException(statusRuntimeException);
224+
}
216225
if (failure instanceof Error) {
217226
throw (Error) failure;
218227
}
@@ -221,6 +230,45 @@ private void convertKnownFailures(Throwable e) {
221230
: new RuntimeException(failure);
222231
}
223232

233+
private HandlerException convertStatusRuntimeExceptionToHandlerException(
234+
StatusRuntimeException sre) {
235+
switch (sre.getStatus().getCode()) {
236+
case INVALID_ARGUMENT:
237+
return new HandlerException(HandlerException.ErrorType.BAD_REQUEST, sre);
238+
case ALREADY_EXISTS:
239+
case FAILED_PRECONDITION:
240+
case OUT_OF_RANGE:
241+
return new HandlerException(
242+
HandlerException.ErrorType.INTERNAL, sre, HandlerException.RetryBehavior.NON_RETRYABLE);
243+
case ABORTED:
244+
case UNAVAILABLE:
245+
return new HandlerException(HandlerException.ErrorType.UNAVAILABLE, sre);
246+
case CANCELLED:
247+
case DATA_LOSS:
248+
case INTERNAL:
249+
case UNKNOWN:
250+
case UNAUTHENTICATED:
251+
case PERMISSION_DENIED:
252+
// Note that codes.Unauthenticated, codes.PermissionDenied have Nexus error types but we
253+
// convert to internal
254+
// because this is not a client auth error and happens when the handler fails to auth with
255+
// Temporal and should
256+
// be considered retryable.
257+
return new HandlerException(HandlerException.ErrorType.INTERNAL, sre);
258+
case NOT_FOUND:
259+
return new HandlerException(HandlerException.ErrorType.NOT_FOUND, sre);
260+
case RESOURCE_EXHAUSTED:
261+
return new HandlerException(HandlerException.ErrorType.RESOURCE_EXHAUSTED, sre);
262+
case UNIMPLEMENTED:
263+
return new HandlerException(HandlerException.ErrorType.NOT_IMPLEMENTED, sre);
264+
case DEADLINE_EXCEEDED:
265+
return new HandlerException(HandlerException.ErrorType.UPSTREAM_TIMEOUT, sre);
266+
default:
267+
// If the status code is not recognized, we treat it as an internal error
268+
return new HandlerException(HandlerException.ErrorType.INTERNAL, sre);
269+
}
270+
}
271+
224272
private OperationStartResult<HandlerResultContent> startOperation(
225273
OperationContext context, OperationStartDetails details, HandlerInputContent input)
226274
throws OperationException {

temporal-sdk/src/test/java/io/temporal/workflow/nexus/OperationFailureConversionTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import io.nexusrpc.handler.OperationHandler;
55
import io.nexusrpc.handler.OperationImpl;
66
import io.nexusrpc.handler.ServiceImpl;
7+
import io.temporal.api.common.v1.WorkflowExecution;
78
import io.temporal.client.WorkflowFailedException;
9+
import io.temporal.client.WorkflowNotFoundException;
810
import io.temporal.failure.ApplicationFailure;
911
import io.temporal.failure.NexusOperationFailure;
1012
import io.temporal.testing.internal.SDKTestWorkflowRule;
@@ -58,6 +60,24 @@ public void nexusOperationApplicationFailureFailureConversion() {
5860
Assert.assertEquals(HandlerException.ErrorType.INTERNAL, handlerFailure.getErrorType());
5961
}
6062

63+
@Test
64+
public void nexusOperationWorkflowNotFoundFailureConversion() {
65+
TestWorkflow1 workflowStub =
66+
testWorkflowRule.newWorkflowStubTimeoutOptions(TestWorkflow1.class);
67+
WorkflowFailedException exception =
68+
Assert.assertThrows(
69+
WorkflowFailedException.class, () -> workflowStub.execute("WorkflowNotFound"));
70+
Assert.assertTrue(exception.getCause() instanceof NexusOperationFailure);
71+
NexusOperationFailure nexusFailure = (NexusOperationFailure) exception.getCause();
72+
Assert.assertTrue(nexusFailure.getCause() instanceof HandlerException);
73+
HandlerException handlerFailure = (HandlerException) nexusFailure.getCause();
74+
Assert.assertEquals(HandlerException.ErrorType.NOT_FOUND, handlerFailure.getErrorType());
75+
Assert.assertTrue(handlerFailure.getCause() instanceof ApplicationFailure);
76+
ApplicationFailure applicationFailure = (ApplicationFailure) handlerFailure.getCause();
77+
Assert.assertEquals(
78+
"io.temporal.client.WorkflowNotFoundException", applicationFailure.getType());
79+
}
80+
6181
public static class TestNexus implements TestWorkflow1 {
6282
@Override
6383
public String execute(String testcase) {
@@ -96,6 +116,9 @@ public OperationHandler<String, String> operation() {
96116
} else if (name.equals("ApplicationFailureNonRetryable")) {
97117
throw ApplicationFailure.newNonRetryableFailure(
98118
"failed to call operation", "TestFailure");
119+
} else if (name.equals("WorkflowNotFound")) {
120+
throw new WorkflowNotFoundException(
121+
WorkflowExecution.getDefaultInstance(), "TestWorkflowType", null);
99122
}
100123
Assert.fail();
101124
return "fail";

0 commit comments

Comments
 (0)