From 4baa72e83b9c442178fc7682713ac849957b9d75 Mon Sep 17 00:00:00 2001
From: Patrick Wrobel <pwrobel@google.com>
Date: Mon, 9 Feb 2026 13:41:48 -0500
Subject: [PATCH 1/3] fix: use StatusProto.fromThrowable for ApiException in
 test-proxy

This should fix the test proxy from prefixing error messages with
"io.grpc.StatusRuntimeException: "
---
 .../bigtable/testproxy/CbtTestProxy.java      | 66 +++----------------
 1 file changed, 8 insertions(+), 58 deletions(-)

diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
index 759f1de86792..92a674634527 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
@@ -284,21 +284,13 @@ public void mutateRow(
       return;
     }
 
-    // TODO(developer): evaluate if we want to manually unpack the proto into a model, instead of
-    // using fromProto. Same for the other methods.
     RowMutation mutation = RowMutation.fromProto(request.getRequest());
     try {
       // This response is empty.
       client.dataClient().mutateRow(mutation);
     } catch (ApiException e) {
       responseObserver.onNext(
-          MutateRowResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          MutateRowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -351,13 +343,7 @@ public void bulkMutateRows(
       return;
     } catch (ApiException e) {
       responseObserver.onNext(
-          MutateRowsResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          MutateRowsResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -410,13 +396,7 @@ public void readRow(ReadRowRequest request, StreamObserver<RowResult> responseOb
       }
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          RowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -461,13 +441,7 @@ public void readRows(ReadRowsRequest request, StreamObserver<RowsResult> respons
           resultBuilder.setStatus(com.google.rpc.Status.getDefaultInstance()).build());
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowsResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          RowsResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -585,13 +559,7 @@ public void sampleRowKeys(
       keyOffsets = client.dataClient().sampleRowKeys(tableId);
     } catch (ApiException e) {
       responseObserver.onNext(
-          SampleRowKeysResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          SampleRowKeysResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -631,13 +599,7 @@ public void checkAndMutateRow(
       matched = client.dataClient().checkAndMutateRow(mutation);
     } catch (ApiException e) {
       responseObserver.onNext(
-          CheckAndMutateRowResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          CheckAndMutateRowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -682,13 +644,7 @@ public void readModifyWriteRow(
       }
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          RowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -745,13 +701,7 @@ public void executeQuery(
       return;
     } catch (ApiException e) {
       responseObserver.onNext(
-          ExecuteQueryResult.newBuilder()
-              .setStatus(
-                  com.google.rpc.Status.newBuilder()
-                      .setCode(e.getStatusCode().getCode().ordinal())
-                      .setMessage(e.getMessage())
-                      .build())
-              .build());
+          ExecuteQueryResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {

From 7d76aacd927a29e1b4e27084f6c0f063e3c19295 Mon Sep 17 00:00:00 2001
From: Patrick Wrobel <pwrobel@google.com>
Date: Mon, 9 Feb 2026 18:20:58 -0500
Subject: [PATCH 2/3] Use convertStatus to handle internal errors as well

---
 .../bigtable/testproxy/CbtTestProxy.java      | 29 ++++++++++++++-----
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
index 5b89b1089bfd..e4d34c62999e 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
@@ -290,7 +290,7 @@ public void mutateRow(
       client.dataClient().mutateRow(mutation);
     } catch (ApiException e) {
       responseObserver.onNext(
-          MutateRowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          MutateRowResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -343,7 +343,7 @@ public void bulkMutateRows(
       return;
     } catch (ApiException e) {
       responseObserver.onNext(
-          MutateRowsResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          MutateRowsResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -396,7 +396,7 @@ public void readRow(ReadRowRequest request, StreamObserver<RowResult> responseOb
       }
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          RowResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -441,7 +441,7 @@ public void readRows(ReadRowsRequest request, StreamObserver<RowsResult> respons
           resultBuilder.setStatus(com.google.rpc.Status.getDefaultInstance()).build());
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowsResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          RowsResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -466,6 +466,7 @@ public void readRows(ReadRowsRequest request, StreamObserver<RowsResult> respons
     responseObserver.onCompleted();
   }
 
+
   /**
    * Helper method to convert row from type com.google.cloud.bigtable.data.v2.models.Row to type
    * com.google.bigtable.v2.Row. After conversion, row cells within the same column and family are
@@ -559,7 +560,7 @@ public void sampleRowKeys(
       keyOffsets = client.dataClient().sampleRowKeys(tableId);
     } catch (ApiException e) {
       responseObserver.onNext(
-          SampleRowKeysResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          SampleRowKeysResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -599,7 +600,7 @@ public void checkAndMutateRow(
       matched = client.dataClient().checkAndMutateRow(mutation);
     } catch (ApiException e) {
       responseObserver.onNext(
-          CheckAndMutateRowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          CheckAndMutateRowResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -644,7 +645,7 @@ public void readModifyWriteRow(
       }
     } catch (ApiException e) {
       responseObserver.onNext(
-          RowResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          RowResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -701,7 +702,7 @@ public void executeQuery(
       return;
     } catch (ApiException e) {
       responseObserver.onNext(
-          ExecuteQueryResult.newBuilder().setStatus(StatusProto.fromThrowable(e)).build());
+          ExecuteQueryResult.newBuilder().setStatus(convertStatus(e)).build());
       responseObserver.onCompleted();
       return;
     } catch (StatusRuntimeException e) {
@@ -795,6 +796,18 @@ public ManagedChannelBuilder apply(ManagedChannelBuilder input) {
         .build();
   }
 
+  // Cleanly forwards server errors through the test proxy. Internal client errors
+  // are wrapped in an UNKNOWN status.
+  private static com.google.rpc.Status convertStatus(ApiException e) {
+    com.google.rpc.Status status = StatusProto.fromThrowable(e);
+    if (status != null) {
+      return status;
+    }
+
+    return com.google.rpc.Status.newBuilder().setCode(e.getStatusCode().getCode().ordinal()).setMessage(e.getMessage())
+        .build();
+  }
+
   private CredentialsProvider getCredentialsProvider(String accessToken) {
     if (accessToken.isEmpty()) {
       return NoCredentialsProvider.create();

From 0a978e5508d6ebb0842d56d261d2e5df49775eab Mon Sep 17 00:00:00 2001
From: Patrick Wrobel <pwrobel@google.com>
Date: Mon, 9 Feb 2026 18:28:54 -0500
Subject: [PATCH 3/3] Clean up newline

---
 .../java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java   | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
index e4d34c62999e..f977a13e58b0 100644
--- a/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
+++ b/test-proxy/src/main/java/com/google/cloud/bigtable/testproxy/CbtTestProxy.java
@@ -466,7 +466,6 @@ public void readRows(ReadRowsRequest request, StreamObserver<RowsResult> respons
     responseObserver.onCompleted();
   }
 
-
   /**
    * Helper method to convert row from type com.google.cloud.bigtable.data.v2.models.Row to type
    * com.google.bigtable.v2.Row. After conversion, row cells within the same column and family are
