This is an automated email from the ASF dual-hosted git repository.

Abacn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 4731dbc5a38 Fix RequestResponseIO parseAndThrow to preserve retryable 
exception types (#37342)
4731dbc5a38 is described below

commit 4731dbc5a38b8d211ed1ac094749f0cb8425edb1
Author: ZIHAN DAI <[email protected]>
AuthorDate: Tue Aug 11 10:51:06 2026 +1000

    Fix RequestResponseIO parseAndThrow to preserve retryable exception types 
(#37342)
---
 .../org/apache/beam/io/requestresponse/Call.java   | 11 ++---
 .../apache/beam/io/requestresponse/CallTest.java   | 51 +++++++++++++++++++++-
 2 files changed, 53 insertions(+), 9 deletions(-)

diff --git 
a/sdks/java/io/rrio/src/main/java/org/apache/beam/io/requestresponse/Call.java 
b/sdks/java/io/rrio/src/main/java/org/apache/beam/io/requestresponse/Call.java
index 616a178d1c3..5b9fa496842 100644
--- 
a/sdks/java/io/rrio/src/main/java/org/apache/beam/io/requestresponse/Call.java
+++ 
b/sdks/java/io/rrio/src/main/java/org/apache/beam/io/requestresponse/Call.java
@@ -596,13 +596,10 @@ class Call<RequestT, ResponseT> extends 
PTransform<PCollection<RequestT>, Result
   private static <T> void parseAndThrow(Future<T> future, ExecutionException e)
       throws UserCodeExecutionException {
     future.cancel(true);
-    if (e.getCause() == null) {
-      throw new UserCodeExecutionException(e);
+    Throwable cause = e.getCause();
+    if (cause instanceof UserCodeExecutionException) {
+      throw (UserCodeExecutionException) cause;
     }
-    Throwable cause = checkStateNotNull(e.getCause());
-    if (cause instanceof UserCodeQuotaException) {
-      throw new UserCodeQuotaException(cause);
-    }
-    throw new UserCodeExecutionException(cause);
+    throw new UserCodeExecutionException(cause == null ? e : cause);
   }
 }
diff --git 
a/sdks/java/io/rrio/src/test/java/org/apache/beam/io/requestresponse/CallTest.java
 
b/sdks/java/io/rrio/src/test/java/org/apache/beam/io/requestresponse/CallTest.java
index 0764ab8db40..5fb20bf38b8 100644
--- 
a/sdks/java/io/rrio/src/test/java/org/apache/beam/io/requestresponse/CallTest.java
+++ 
b/sdks/java/io/rrio/src/test/java/org/apache/beam/io/requestresponse/CallTest.java
@@ -104,6 +104,20 @@ public class CallTest {
     pipeline.run();
   }
 
+  @Test
+  public void 
givenCallerThrowsNonUserCodeException_emitsWrappedUserCodeExecutionException() {
+    Result<Response> result =
+        pipeline
+            .apply(Create.of(new Request("a")))
+            .apply(Call.of(new CallerThrowsRuntimeException(), 
NON_DETERMINISTIC_RESPONSE_CODER));
+
+    PCollection<ApiIOError> failures = result.getFailures();
+    PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeExecutionException.class))
+        .isEqualTo(1L);
+
+    pipeline.run();
+  }
+
   @Test
   public void givenCallerThrowsQuotaException_emitsIntoFailurePCollection() {
     Result<Response> result =
@@ -142,7 +156,7 @@ public class CallTest {
   }
 
   @Test
-  public void givenCallerThrowsTimeoutException_emitsFailurePCollection() {
+  public void givenCallerThrowsTimeoutException_thenPreservesExceptionType() {
     Result<Response> result =
         pipeline
             .apply(Create.of(new Request("a")))
@@ -150,7 +164,7 @@ public class CallTest {
 
     PCollection<ApiIOError> failures = result.getFailures();
     PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeExecutionException.class))
-        .isEqualTo(1L);
+        .isEqualTo(0L);
     PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeQuotaException.class)).isEqualTo(0L);
     PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeTimeoutException.class))
         .isEqualTo(1L);
@@ -158,6 +172,23 @@ public class CallTest {
     pipeline.run();
   }
 
+  @Test
+  public void 
givenCallerThrowsRemoteSystemException_thenPreservesExceptionType() {
+    Result<Response> result =
+        pipeline
+            .apply(Create.of(new Request("a")))
+            .apply(
+                Call.of(new CallerThrowsRemoteSystemException(), 
NON_DETERMINISTIC_RESPONSE_CODER));
+
+    PCollection<ApiIOError> failures = result.getFailures();
+    PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeRemoteSystemException.class))
+        .isEqualTo(1L);
+    PAssert.thatSingleton(countStackTracesOf(failures, 
UserCodeExecutionException.class))
+        .isEqualTo(0L);
+
+    pipeline.run();
+  }
+
   @Test
   public void givenSetupThrowsUserCodeExecutionException_throwsError() {
     pipeline
@@ -375,6 +406,14 @@ public class CallTest {
     }
   }
 
+  private static class CallerThrowsRuntimeException implements Caller<Request, 
Response> {
+
+    @Override
+    public Response call(Request request) {
+      throw new RuntimeException("unexpected error");
+    }
+  }
+
   private static class CallerThrowsTimeout implements Caller<Request, 
Response> {
 
     @Override
@@ -383,6 +422,14 @@ public class CallTest {
     }
   }
 
+  private static class CallerThrowsRemoteSystemException implements 
Caller<Request, Response> {
+
+    @Override
+    public Response call(Request request) throws UserCodeExecutionException {
+      throw new UserCodeRemoteSystemException("");
+    }
+  }
+
   private static class CallerInvokesQuotaException implements Caller<Request, 
Response> {
 
     @Override

Reply via email to