From 97672a99b8abb3bea833340d5a496b5304dd2139 Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Mon, 10 Aug 2026 03:48:13 +0000 Subject: [PATCH 1/4] feat(gax): propagate structured LRO error details to ApiException --- .../v1beta1/it/ITLongRunningOperation.java | 39 +++++++++++++++++++ .../api/gax/grpc/GrpcOperationSnapshot.java | 10 +++++ .../gax/grpc/ProtoOperationTransformers.java | 3 +- .../grpc/ProtoOperationTransformersTest.java | 20 ++++++++++ .../httpjson/HttpJsonOperationSnapshot.java | 25 +++++++++++- .../httpjson/ProtoOperationTransformers.java | 3 +- .../HttpJsonOperationSnapshotTest.java | 22 +++++++++++ .../ProtoOperationTransformersTest.java | 20 ++++++++++ .../gax/longrunning/OperationSnapshot.java | 12 ++++++ 9 files changed, 150 insertions(+), 4 deletions(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java index 09208861babd..8876d85ced4f 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java @@ -20,13 +20,19 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.retrying.RetrySettings; +import com.google.api.gax.rpc.ApiException; +import com.google.protobuf.Any; import com.google.protobuf.Timestamp; +import com.google.rpc.Code; +import com.google.rpc.Status; import com.google.showcase.v1beta1.EchoClient; +import com.google.showcase.v1beta1.PoetryError; import com.google.showcase.v1beta1.WaitMetadata; import com.google.showcase.v1beta1.WaitRequest; import com.google.showcase.v1beta1.WaitResponse; import com.google.showcase.v1beta1.it.util.TestClientInitializer; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.threeten.bp.Duration; @@ -193,4 +199,37 @@ void testHttpJson_LROUnsuccessfulResponse_exceedsTotalTimeout_throwsDeadlineExce TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } } + + @Test + void testGRPC_LROErrorResponse_dropsErrorDetails() throws Exception { + EchoClient grpcClient = TestClientInitializer.createGrpcEchoClient(); + try { + PoetryError poetryError = + PoetryError.newBuilder().setPoem("Roses are red, violets are blue").build(); + Status status = + Status.newBuilder() + .setCode(Code.ALREADY_EXISTS_VALUE) + .setMessage("The resource already exists") + .addDetails(Any.pack(poetryError)) + .build(); + WaitRequest waitRequest = WaitRequest.newBuilder().setError(status).build(); + OperationFuture operationFuture = + grpcClient.waitOperationCallable().futureCall(waitRequest); + ExecutionException exception = assertThrows(ExecutionException.class, operationFuture::get); + assertThat(exception.getCause()).isInstanceOf(ApiException.class); + ApiException apiException = (ApiException) exception.getCause(); + + // Verify that error details are successfully propagated + assertThat(apiException.getErrorDetails()).isNotNull(); + PoetryError unpackedError = apiException.getErrorDetails().getMessage(PoetryError.class); + assertThat(unpackedError).isNotNull(); + assertThat(unpackedError.getPoem()).isEqualTo("Roses are red, violets are blue"); + } finally { + grpcClient.close(); + grpcClient.awaitTermination( + TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); + } + } + + } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java index 9f271e57f0b8..77f68b186ddf 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java @@ -30,9 +30,11 @@ package com.google.api.gax.grpc; import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode; import com.google.longrunning.Operation; import io.grpc.Status; +import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; /** @@ -79,6 +81,14 @@ public String getErrorMessage() { return operation.getError().getMessage(); } + @Override + public @Nullable ErrorDetails getErrorDetails() { + if (operation.hasError()) { + return ErrorDetails.builder().setRawErrorMessages(operation.getError().getDetailsList()).build(); + } + return null; + } + public static GrpcOperationSnapshot create(Operation operation) { return new GrpcOperationSnapshot(operation); } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ProtoOperationTransformers.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ProtoOperationTransformers.java index 425842b17499..dfb4105b4755 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ProtoOperationTransformers.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/ProtoOperationTransformers.java @@ -63,7 +63,8 @@ public ResponseT apply(OperationSnapshot operationSnapshot) { + operationSnapshot.getErrorMessage(), null, operationSnapshot.getErrorCode(), - false); + false, + operationSnapshot.getErrorDetails()); } if (!(operationSnapshot.getResponse() instanceof Any)) { diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java index 19122ff653c0..b92768cf40f0 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java @@ -110,4 +110,24 @@ void testAnyMetadataTransformer_mismatchedTypes() { assertThrows(UnknownException.class, () -> transformer.apply(operationSnapshot)); Truth.assertThat(exception).hasMessageThat().contains("encountered a problem unpacking it"); } + + @Test + void testAnyResponseTransformer_exceptionWithErrorDetails() { + ResponseTransformer transformer = ResponseTransformer.create(Money.class); + Money inputMoney = Money.newBuilder().setCurrencyCode("USD").build(); + Color poetryError = Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload + Status status = + Status.newBuilder() + .setCode(Code.UNAVAILABLE.value()) + .addDetails(Any.pack(poetryError)) + .build(); + OperationSnapshot operationSnapshot = + GrpcOperationSnapshot.create( + Operation.newBuilder().setResponse(Any.pack(inputMoney)).setError(status).build()); + + UnavailableException exception = + assertThrows(UnavailableException.class, () -> transformer.apply(operationSnapshot)); + Truth.assertThat(exception.getErrorDetails()).isNotNull(); + Truth.assertThat(exception.getErrorDetails().getMessage(Color.class)).isEqualTo(poetryError); + } } diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java index d709b680a9b6..311356688ac5 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java @@ -31,9 +31,11 @@ import com.google.api.core.InternalApi; import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StatusCode.Code; import com.google.longrunning.Operation; +import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; /** @@ -50,6 +52,7 @@ public class HttpJsonOperationSnapshot implements OperationSnapshot { private final Object response; private final StatusCode errorCode; private final String errorMessage; + private final @Nullable ErrorDetails errorDetails; private HttpJsonOperationSnapshot( String name, @@ -57,13 +60,15 @@ private HttpJsonOperationSnapshot( boolean done, Object response, StatusCode errorCode, - String errorMessage) { + String errorMessage, + @Nullable ErrorDetails errorDetails) { this.name = name; this.metadata = metadata; this.done = done; this.response = response; this.errorCode = errorCode; this.errorMessage = errorMessage; + this.errorDetails = errorDetails; } /** {@inheritDoc} */ @@ -102,6 +107,11 @@ public String getErrorMessage() { return this.errorMessage; } + @Override + public @Nullable ErrorDetails getErrorDetails() { + return this.errorDetails; + } + public static HttpJsonOperationSnapshot create(Operation operation) { return newBuilder().setOperation(operation).build(); } @@ -117,6 +127,12 @@ public static class Builder { private Object response; private StatusCode errorCode; private String errorMessage; + private @Nullable ErrorDetails errorDetails; + + public Builder setErrorDetails(@Nullable ErrorDetails errorDetails) { + this.errorDetails = errorDetails; + return this; + } public Builder setName(String name) { this.name = name; @@ -153,11 +169,16 @@ private Builder setOperation(Operation operation) { this.errorCode = HttpJsonStatusCode.of(com.google.rpc.Code.forNumber(operation.getError().getCode())); this.errorMessage = operation.getError().getMessage(); + if (operation.hasError()) { + this.errorDetails = + ErrorDetails.builder().setRawErrorMessages(operation.getError().getDetailsList()).build(); + } return this; } public HttpJsonOperationSnapshot build() { - return new HttpJsonOperationSnapshot(name, metadata, done, response, errorCode, errorMessage); + return new HttpJsonOperationSnapshot( + name, metadata, done, response, errorCode, errorMessage, errorDetails); } } } diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java index d5092cbad9ec..cc9200b87763 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoOperationTransformers.java @@ -63,7 +63,8 @@ public ResponseT apply(OperationSnapshot operationSnapshot) { + operationSnapshot.getErrorMessage(), null, operationSnapshot.getErrorCode(), - false); + false, + operationSnapshot.getErrorDetails()); } if (!(operationSnapshot.getResponse() instanceof Any)) { diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java index 2dda22e61905..40b67b266a12 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java @@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode.Code; import java.util.ArrayList; import org.junit.jupiter.api.Test; @@ -86,4 +87,25 @@ void newBuilderTestNotDone() { assertEquals(HttpJsonStatusCode.of(Code.OK), testOperationSnapshot.getErrorCode()); assertFalse(testOperationSnapshot.isDone()); } + + @Test + void newBuilderTestWithErrorDetails() { + ErrorDetails errorDetails = + ErrorDetails.builder() + .setRawErrorMessages( + java.util.Collections.singletonList( + com.google.protobuf.Any.pack( + com.google.protobuf.Empty.getDefaultInstance()))) + .build(); + HttpJsonOperationSnapshot testOperationSnapshot = + HttpJsonOperationSnapshot.newBuilder() + .setName("snapshot-details") + .setMetadata("Dallas") + .setDone(true) + .setError(400, "Bad Request") + .setErrorDetails(errorDetails) + .build(); + + assertEquals(errorDetails, testOperationSnapshot.getErrorDetails()); + } } diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java index 8220e5e3d4f9..e2388244dca0 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java @@ -142,4 +142,24 @@ void testAnyMetadataTransformer_mismatchedTypes() { assertThrows(UnknownException.class, () -> transformer.apply(operationSnapshot)); Truth.assertThat(exception).hasMessageThat().contains("encountered a problem unpacking it"); } + + @Test + void testAnyResponseTransformer_exceptionWithErrorDetails() { + ResponseTransformer transformer = ResponseTransformer.create(Money.class); + Money inputMoney = Money.newBuilder().setCurrencyCode("USD").build(); + Color poetryError = Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload + Status status = + Status.newBuilder() + .setCode(Code.UNAVAILABLE.getNumber()) + .addDetails(Any.pack(poetryError)) + .build(); + OperationSnapshot operationSnapshot = + HttpJsonOperationSnapshot.create( + Operation.newBuilder().setResponse(Any.pack(inputMoney)).setError(status).build()); + + UnavailableException exception = + assertThrows(UnavailableException.class, () -> transformer.apply(operationSnapshot)); + Truth.assertThat(exception.getErrorDetails()).isNotNull(); + Truth.assertThat(exception.getErrorDetails().getMessage(Color.class)).isEqualTo(poetryError); + } } diff --git a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java index 92f7aa04cbf3..42194d44f437 100644 --- a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java @@ -29,7 +29,9 @@ */ package com.google.api.gax.longrunning; +import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode; +import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; /** @@ -67,4 +69,14 @@ public interface OperationSnapshot { * or if it succeeded, returns null. */ String getErrorMessage(); + + /** + * If the operation is done and it failed, returns the ErrorDetails; if the + * operation is not done or if it succeeded, returns null. + * + * @return the error details if the operation failed, null otherwise + */ + default @Nullable ErrorDetails getErrorDetails() { + return null; + } } From 532984901bf592cda9b38448a4050b36475088ef Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Mon, 10 Aug 2026 04:00:27 +0000 Subject: [PATCH 2/4] fix: updated http/json snapshot to adhere to monorepo styling --- .../showcase/v1beta1/it/ITLongRunningOperation.java | 2 -- .../api/gax/httpjson/HttpJsonOperationSnapshotTest.java | 9 ++++++--- .../google/api/gax/longrunning/OperationSnapshot.java | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java index 8876d85ced4f..51e507ca8f23 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java @@ -230,6 +230,4 @@ void testGRPC_LROErrorResponse_dropsErrorDetails() throws Exception { TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS); } } - - } diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java index 40b67b266a12..a76b3793fe24 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java @@ -35,7 +35,10 @@ import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode.Code; +import com.google.protobuf.Any; +import com.google.protobuf.Empty; import java.util.ArrayList; +import java.util.Collections; import org.junit.jupiter.api.Test; class HttpJsonOperationSnapshotTest { @@ -93,9 +96,9 @@ void newBuilderTestWithErrorDetails() { ErrorDetails errorDetails = ErrorDetails.builder() .setRawErrorMessages( - java.util.Collections.singletonList( - com.google.protobuf.Any.pack( - com.google.protobuf.Empty.getDefaultInstance()))) + Collections.singletonList( + Any.pack( + Empty.getDefaultInstance()))) .build(); HttpJsonOperationSnapshot testOperationSnapshot = HttpJsonOperationSnapshot.newBuilder() diff --git a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java index 42194d44f437..fe20877f9381 100644 --- a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/longrunning/OperationSnapshot.java @@ -31,8 +31,8 @@ import com.google.api.gax.rpc.ErrorDetails; import com.google.api.gax.rpc.StatusCode; -import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * A snapshot of a long-running operation. @@ -71,8 +71,8 @@ public interface OperationSnapshot { String getErrorMessage(); /** - * If the operation is done and it failed, returns the ErrorDetails; if the - * operation is not done or if it succeeded, returns null. + * If the operation is done and it failed, returns the ErrorDetails; if the operation is not done + * or if it succeeded, returns null. * * @return the error details if the operation failed, null otherwise */ From f6913dffcdda057966caea60f0a553c88cbfc42b Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Mon, 10 Aug 2026 04:19:18 +0000 Subject: [PATCH 3/4] fix: updated following gemini feedback --- .../v1beta1/it/ITLongRunningOperation.java | 2 +- .../api/gax/grpc/GrpcOperationSnapshot.java | 8 +++++--- .../gax/grpc/ProtoOperationTransformersTest.java | 3 ++- .../gax/httpjson/HttpJsonOperationSnapshot.java | 16 ++++++++++++---- .../httpjson/HttpJsonOperationSnapshotTest.java | 5 +---- .../httpjson/ProtoOperationTransformersTest.java | 3 ++- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java index 51e507ca8f23..f3a4a528a76f 100644 --- a/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java +++ b/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITLongRunningOperation.java @@ -201,7 +201,7 @@ void testHttpJson_LROUnsuccessfulResponse_exceedsTotalTimeout_throwsDeadlineExce } @Test - void testGRPC_LROErrorResponse_dropsErrorDetails() throws Exception { + void testGRPC_LROErrorResponse_propagatesErrorDetails() throws Exception { EchoClient grpcClient = TestClientInitializer.createGrpcEchoClient(); try { PoetryError poetryError = diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java index 77f68b186ddf..58f773696d32 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcOperationSnapshot.java @@ -34,8 +34,8 @@ import com.google.api.gax.rpc.StatusCode; import com.google.longrunning.Operation; import io.grpc.Status; -import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Implementation of OperationSnapshot based on gRPC. @@ -83,8 +83,10 @@ public String getErrorMessage() { @Override public @Nullable ErrorDetails getErrorDetails() { - if (operation.hasError()) { - return ErrorDetails.builder().setRawErrorMessages(operation.getError().getDetailsList()).build(); + if (operation.hasError() && operation.getError().getDetailsCount() > 0) { + return ErrorDetails.builder() + .setRawErrorMessages(operation.getError().getDetailsList()) + .build(); } return null; } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java index b92768cf40f0..0ec1c3e14f0e 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/ProtoOperationTransformersTest.java @@ -115,7 +115,8 @@ void testAnyMetadataTransformer_mismatchedTypes() { void testAnyResponseTransformer_exceptionWithErrorDetails() { ResponseTransformer transformer = ResponseTransformer.create(Money.class); Money inputMoney = Money.newBuilder().setCurrencyCode("USD").build(); - Color poetryError = Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload + Color poetryError = + Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload Status status = Status.newBuilder() .setCode(Code.UNAVAILABLE.value()) diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java index 311356688ac5..792a10b65390 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java @@ -35,8 +35,8 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StatusCode.Code; import com.google.longrunning.Operation; -import org.jspecify.annotations.Nullable; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Implementation of OperationSnapshot based on REST transport. @@ -129,7 +129,13 @@ public static class Builder { private String errorMessage; private @Nullable ErrorDetails errorDetails; - public Builder setErrorDetails(@Nullable ErrorDetails errorDetails) { + /** + * Sets the LRO error details. + * + * @param errorDetails the LRO error details + * @return the builder instance + */ + public Builder setErrorDetails(final @Nullable ErrorDetails errorDetails) { this.errorDetails = errorDetails; return this; } @@ -169,9 +175,11 @@ private Builder setOperation(Operation operation) { this.errorCode = HttpJsonStatusCode.of(com.google.rpc.Code.forNumber(operation.getError().getCode())); this.errorMessage = operation.getError().getMessage(); - if (operation.hasError()) { + if (operation.hasError() && operation.getError().getDetailsCount() > 0) { this.errorDetails = - ErrorDetails.builder().setRawErrorMessages(operation.getError().getDetailsList()).build(); + ErrorDetails.builder() + .setRawErrorMessages(operation.getError().getDetailsList()) + .build(); } return this; } diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java index a76b3793fe24..99e3f672bdd0 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshotTest.java @@ -95,10 +95,7 @@ void newBuilderTestNotDone() { void newBuilderTestWithErrorDetails() { ErrorDetails errorDetails = ErrorDetails.builder() - .setRawErrorMessages( - Collections.singletonList( - Any.pack( - Empty.getDefaultInstance()))) + .setRawErrorMessages(Collections.singletonList(Any.pack(Empty.getDefaultInstance()))) .build(); HttpJsonOperationSnapshot testOperationSnapshot = HttpJsonOperationSnapshot.newBuilder() diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java index e2388244dca0..4306bddcba4e 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoOperationTransformersTest.java @@ -147,7 +147,8 @@ void testAnyMetadataTransformer_mismatchedTypes() { void testAnyResponseTransformer_exceptionWithErrorDetails() { ResponseTransformer transformer = ResponseTransformer.create(Money.class); Money inputMoney = Money.newBuilder().setCurrencyCode("USD").build(); - Color poetryError = Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload + Color poetryError = + Color.newBuilder().setRed(1.0f).build(); // Use Color as a mock details payload Status status = Status.newBuilder() .setCode(Code.UNAVAILABLE.getNumber()) From 5607daeb36c8a296025765a99495015b047f32dd Mon Sep 17 00:00:00 2001 From: Nicole Lee Date: Mon, 10 Aug 2026 04:34:17 +0000 Subject: [PATCH 4/4] added else block to prevent stale errorDetails --- .../com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java index 792a10b65390..c700cec9f595 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonOperationSnapshot.java @@ -180,6 +180,8 @@ private Builder setOperation(Operation operation) { ErrorDetails.builder() .setRawErrorMessages(operation.getError().getDetailsList()) .build(); + } else { + this.errorDetails = null; } return this; }