You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by li...@apache.org on 2026/10/15 09:59:28 UTC

(rocketmq-dashboard) branch rocketmq-studio updated: fix(aliyun): restore read and catalog response validation (#6565)

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

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 3c9b6f728 fix(aliyun): restore read and catalog response validation (#6565)
3c9b6f728 is described below

commit 3c9b6f728234d790d969405b0b04ccc0f13bf638
Author: zmuxuny <xi...@gmail.com>
AuthorDate: Thu Oct 15 02:59:22 2026 -0700

    fix(aliyun): restore read and catalog response validation (#6565)
---
 .../common/util/AliyunResponseValidator.java       |  48 ++++++
 .../provider/alibaba/AliyunCatalogService.java     |  22 ++-
 .../provider/alibaba/AliyunInstanceProvider.java   |  64 +++++--
 .../exception/GlobalExceptionHandlerTest.java      |   8 +
 .../common/util/AliyunResponseValidatorTest.java   |  73 ++++++++
 .../provider/alibaba/AliyunCatalogServiceTest.java |  98 ++++++++++-
 .../alibaba/AliyunInstanceProviderTest.java        | 191 ++++++++++++++++++---
 7 files changed, 450 insertions(+), 54 deletions(-)

diff --git a/server/src/main/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidator.java b/server/src/main/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidator.java
new file mode 100644
index 000000000..38e2588cb
--- /dev/null
+++ b/server/src/main/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidator.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.common.util;
+
+import org.apache.rocketmq.studio.common.exception.BusinessException;
+import org.springframework.util.StringUtils;
+
+import java.util.function.Function;
+
+/** Shared, null-tolerant business flag validation for Aliyun read and catalog responses. */
+public final class AliyunResponseValidator {
+    private AliyunResponseValidator() {
+    }
+
+    /**
+     * Missing response bodies are malformed upstream responses. A present body with an
+     * omitted success flag remains compatible; only explicit false is a business rejection.
+     * Business rejections use 422 rather than a transport/outage 502 or a Studio-session 401.
+     */
+    public static <T> T requireReadableBody(String operation, T body, Function<T, Boolean> success,
+            Function<T, String> code, Function<T, String> message) {
+        if (body == null) {
+            throw new BusinessException(502, "Aliyun " + operation + " returned an empty response body");
+        }
+        if (Boolean.FALSE.equals(success.apply(body))) {
+            String providerMessage = message.apply(body);
+            String providerCode = code.apply(body);
+            String detail = StringUtils.hasText(providerMessage) ? providerMessage
+                    : StringUtils.hasText(providerCode) ? providerCode : "provider rejected the request";
+            throw new BusinessException(422, "Aliyun " + operation + " rejected: " + detail);
+        }
+        return body;
+    }
+}
diff --git a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogService.java b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogService.java
index 69696884c..561a85b28 100644
--- a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogService.java
+++ b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogService.java
@@ -27,6 +27,7 @@ import com.aliyun.sdk.service.rocketmq20220801.models.ListRegionsResponse;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListRegionsResponseBody;
 import org.apache.rocketmq.studio.common.domain.enums.InstanceVendor;
 import org.apache.rocketmq.studio.common.exception.BusinessException;
+import org.apache.rocketmq.studio.common.util.AliyunResponseValidator;
 import org.apache.rocketmq.studio.provider.CloudCatalogProvider;
 import org.apache.rocketmq.studio.provider.CloudInstanceDetailVO;
 import org.apache.rocketmq.studio.provider.CloudInstanceOptionVO;
@@ -63,8 +64,11 @@ public class AliyunCatalogService implements CloudCatalogProvider {
         requireId(credentialId, "credentialId");
         ListRegionsResponse response = clientFactory.call(credentialId, DEFAULT_REGION,
                 client -> client.listRegions(ListRegionsRequest.builder().build()));
-        ListRegionsResponseBody body = response == null ? null : response.getBody();
-        List<ListRegionsResponseBody.Data> data = body == null ? null : body.getData();
+        ListRegionsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "region listing", response == null ? null : response.getBody(),
+                ListRegionsResponseBody::getSuccess, ListRegionsResponseBody::getCode,
+                ListRegionsResponseBody::getMessage);
+        List<ListRegionsResponseBody.Data> data = body.getData();
         List<CloudRegionVO> regions = new ArrayList<>();
         if (data == null) {
             return regions;
@@ -109,8 +113,11 @@ public class AliyunCatalogService implements CloudCatalogProvider {
         GetInstanceRequest request = GetInstanceRequest.builder().instanceId(normalizedCloudInstanceId).build();
         GetInstanceResponse response = clientFactory.call(credentialId, normalizedRegionId,
                 client -> client.getInstance(request));
-        GetInstanceResponseBody body = response == null ? null : response.getBody();
-        GetInstanceResponseBody.Data data = body == null ? null : body.getData();
+        GetInstanceResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "instance lookup", response == null ? null : response.getBody(),
+                GetInstanceResponseBody::getSuccess, GetInstanceResponseBody::getCode,
+                GetInstanceResponseBody::getMessage);
+        GetInstanceResponseBody.Data data = body.getData();
         if (data == null) {
             throw new BusinessException(404, "Aliyun instance not found: " + normalizedCloudInstanceId);
         }
@@ -126,8 +133,11 @@ public class AliyunCatalogService implements CloudCatalogProvider {
                     .build();
             ListInstancesResponse response = clientFactory.call(credentialId, regionId,
                     client -> client.listInstances(request));
-            ListInstancesResponseBody body = response == null ? null : response.getBody();
-            ListInstancesResponseBody.Data data = body == null ? null : body.getData();
+            ListInstancesResponseBody body = AliyunResponseValidator.requireReadableBody(
+                    "instance listing", response == null ? null : response.getBody(),
+                    ListInstancesResponseBody::getSuccess, ListInstancesResponseBody::getCode,
+                    ListInstancesResponseBody::getMessage);
+            ListInstancesResponseBody.Data data = body.getData();
             List<ListInstancesResponseBody.List> list = data == null ? null : data.getList();
             if (list == null || list.isEmpty()) {
                 break;
diff --git a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
index d1c10c0c3..800d011bb 100644
--- a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
+++ b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
@@ -54,6 +54,7 @@ import org.springframework.util.StringUtils;
 import org.apache.rocketmq.studio.common.domain.PageResult;
 import org.apache.rocketmq.studio.common.domain.enums.InstanceVendor;
 import org.apache.rocketmq.studio.common.exception.BusinessException;
+import org.apache.rocketmq.studio.common.util.AliyunResponseValidator;
 import org.apache.rocketmq.studio.common.util.Pagination;
 import org.apache.rocketmq.studio.instance.InstanceRepository;
 import org.apache.rocketmq.studio.instance.InstanceVO;
@@ -129,8 +130,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         ListTopicsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.listTopics(request));
-        ListTopicsResponseBody body = response == null ? null : response.getBody();
-        ListTopicsResponseBody.Data data = body == null ? null : body.getData();
+        ListTopicsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "topic listing", response == null ? null : response.getBody(),
+                ListTopicsResponseBody::getSuccess, ListTopicsResponseBody::getCode,
+                ListTopicsResponseBody::getMessage);
+        ListTopicsResponseBody.Data data = body.getData();
         Long totalCount = data == null ? null : data.getTotalCount();
         return totalCount == null || totalCount < 0
                 ? listTopics(instanceId, null, null).size()
@@ -147,8 +151,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         ListConsumerGroupsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.listConsumerGroups(request));
-        ListConsumerGroupsResponseBody body = response == null ? null : response.getBody();
-        ListConsumerGroupsResponseBody.Data data = body == null ? null : body.getData();
+        ListConsumerGroupsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "consumer group listing", response == null ? null : response.getBody(),
+                ListConsumerGroupsResponseBody::getSuccess, ListConsumerGroupsResponseBody::getCode,
+                ListConsumerGroupsResponseBody::getMessage);
+        ListConsumerGroupsResponseBody.Data data = body.getData();
         Long totalCount = data == null ? null : data.getTotalCount();
         return totalCount == null || totalCount < 0
                 ? listConsumerGroups(instanceId, null).size()
@@ -194,8 +201,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
         ListTopicsRequest request = buildTopicRequest(ctx.cloudInstanceId(), type, search, page, pageSize);
         ListTopicsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.listTopics(request));
-        ListTopicsResponseBody body = response == null ? null : response.getBody();
-        return body == null ? null : body.getData();
+        ListTopicsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "topic listing", response == null ? null : response.getBody(),
+                ListTopicsResponseBody::getSuccess, ListTopicsResponseBody::getCode,
+                ListTopicsResponseBody::getMessage);
+        return body.getData();
     }
 
     private ListTopicsRequest buildTopicRequest(String cloudInstanceId, String type, String search,
@@ -296,8 +306,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         ListTopicSubscriptionsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.listTopicSubscriptions(request));
-        ListTopicSubscriptionsResponseBody body = response == null ? null : response.getBody();
-        List<ListTopicSubscriptionsResponseBody.Data> data = body == null ? null : body.getData();
+        ListTopicSubscriptionsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "topic subscription query", response == null ? null : response.getBody(),
+                ListTopicSubscriptionsResponseBody::getSuccess, ListTopicSubscriptionsResponseBody::getCode,
+                ListTopicSubscriptionsResponseBody::getMessage);
+        List<ListTopicSubscriptionsResponseBody.Data> data = body.getData();
         List<TopicConsumerVO> consumers = new ArrayList<>();
         if (data == null) {
             return consumers;
@@ -326,8 +339,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
             ListConsumerGroupsRequest request = builder.build();
             ListConsumerGroupsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                     client -> client.listConsumerGroups(request));
-            ListConsumerGroupsResponseBody body = response == null ? null : response.getBody();
-            ListConsumerGroupsResponseBody.Data data = body == null ? null : body.getData();
+            ListConsumerGroupsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                    "consumer group listing", response == null ? null : response.getBody(),
+                    ListConsumerGroupsResponseBody::getSuccess, ListConsumerGroupsResponseBody::getCode,
+                    ListConsumerGroupsResponseBody::getMessage);
+            ListConsumerGroupsResponseBody.Data data = body.getData();
             List<ListConsumerGroupsResponseBody.List> list = data == null ? null : data.getList();
             if (list == null || list.isEmpty()) {
                 break;
@@ -423,8 +439,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         GetConsumerGroupLagResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.getConsumerGroupLag(request));
-        GetConsumerGroupLagResponseBody body = response == null ? null : response.getBody();
-        GetConsumerGroupLagResponseBody.Data data = body == null ? null : body.getData();
+        GetConsumerGroupLagResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "consumer lag query", response == null ? null : response.getBody(),
+                GetConsumerGroupLagResponseBody::getSuccess, GetConsumerGroupLagResponseBody::getCode,
+                GetConsumerGroupLagResponseBody::getMessage);
+        GetConsumerGroupLagResponseBody.Data data = body.getData();
         if (data == null) {
             return new ArrayList<>();
         }
@@ -440,8 +459,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         ListConsumerGroupSubscriptionsResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.listConsumerGroupSubscriptions(request));
-        ListConsumerGroupSubscriptionsResponseBody body = response == null ? null : response.getBody();
-        List<ListConsumerGroupSubscriptionsResponseBody.Data> data = body == null ? null : body.getData();
+        ListConsumerGroupSubscriptionsResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "consumer subscription query", response == null ? null : response.getBody(),
+                ListConsumerGroupSubscriptionsResponseBody::getSuccess, ListConsumerGroupSubscriptionsResponseBody::getCode,
+                ListConsumerGroupSubscriptionsResponseBody::getMessage);
+        List<ListConsumerGroupSubscriptionsResponseBody.Data> data = body.getData();
         List<SubscriptionEntryVO> subscriptions = new ArrayList<>();
         if (data == null) {
             return subscriptions;
@@ -534,8 +556,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
             ListMessagesRequest request = builder.build();
             ListMessagesResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                     client -> client.listMessages(request));
-            ListMessagesResponseBody body = response == null ? null : response.getBody();
-            ListMessagesResponseBody.Data data = body == null ? null : body.getData();
+            ListMessagesResponseBody body = AliyunResponseValidator.requireReadableBody(
+                    "message query", response == null ? null : response.getBody(),
+                    ListMessagesResponseBody::getSuccess, ListMessagesResponseBody::getCode,
+                    ListMessagesResponseBody::getMessage);
+            ListMessagesResponseBody.Data data = body.getData();
             List<ListMessagesResponseBody.List> list = data == null ? null : data.getList();
             if (list == null || list.isEmpty()) {
                 break;
@@ -602,8 +627,11 @@ public class AliyunInstanceProvider implements InstanceProvider {
                 .build();
         GetTraceResponse response = clientFactory.call(ctx.credentialId(), ctx.regionId(),
                 client -> client.getTrace(request));
-        GetTraceResponseBody body = response == null ? null : response.getBody();
-        GetTraceResponseBody.Data data = body == null ? null : body.getData();
+        GetTraceResponseBody body = AliyunResponseValidator.requireReadableBody(
+                "message trace query", response == null ? null : response.getBody(),
+                GetTraceResponseBody::getSuccess, GetTraceResponseBody::getCode,
+                GetTraceResponseBody::getMessage);
+        GetTraceResponseBody.Data data = body.getData();
         if (data == null) {
             return emptyTraceRecord();
         }
diff --git a/server/src/test/java/org/apache/rocketmq/studio/common/exception/GlobalExceptionHandlerTest.java b/server/src/test/java/org/apache/rocketmq/studio/common/exception/GlobalExceptionHandlerTest.java
index 61b7b832e..583434710 100644
--- a/server/src/test/java/org/apache/rocketmq/studio/common/exception/GlobalExceptionHandlerTest.java
+++ b/server/src/test/java/org/apache/rocketmq/studio/common/exception/GlobalExceptionHandlerTest.java
@@ -118,4 +118,12 @@ class GlobalExceptionHandlerTest {
             throw new HttpMediaTypeNotAcceptableException(List.of(MediaType.APPLICATION_JSON));
         }
     }
+    @Test
+    void preservesBusinessRejectionWithoutPromotingItToGatewayFailureTest() throws Exception {
+        mockMvc.perform(get("/test/business/422"))
+                .andExpect(status().is(422))
+                .andExpect(jsonPath("$.code").value(422))
+                .andExpect(jsonPath("$.message").value("failure-422"));
+    }
+
 }
diff --git a/server/src/test/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidatorTest.java b/server/src/test/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidatorTest.java
new file mode 100644
index 000000000..d4ecbaac0
--- /dev/null
+++ b/server/src/test/java/org/apache/rocketmq/studio/common/util/AliyunResponseValidatorTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.common.util;
+
+import org.apache.rocketmq.studio.common.exception.BusinessException;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class AliyunResponseValidatorTest {
+    @ParameterizedTest
+    @NullSource
+    @ValueSource(booleans = {true})
+    void presentBodyWithTrueOrOmittedFlagRemainsReadableTest(Boolean success) {
+        Body body = new Body(success, "ignored-code", "ignored-message");
+        assertThat(validate(body)).isSameAs(body);
+    }
+
+    @Test
+    void explicitFailureIsBusinessRejectionWithProviderMessageTest() {
+        assertThatThrownBy(() -> validate(new Body(false, "Denied", "permission denied")))
+                .isInstanceOf(BusinessException.class).hasMessage("Aliyun test read rejected: permission denied")
+                .extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void blankMessageFallsBackToProviderCodeTest() {
+        assertThatThrownBy(() -> validate(new Body(false, "Denied", "  ")))
+                .isInstanceOf(BusinessException.class).hasMessage("Aliyun test read rejected: Denied")
+                .extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void missingMessageAndCodeUseBusinessFallbackTest() {
+        assertThatThrownBy(() -> validate(new Body(false, null, null)))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Aliyun test read rejected: provider rejected the request")
+                .extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void missingBodyIsMalformedUpstreamResponseTest() {
+        assertThatThrownBy(() -> validate(null)).isInstanceOf(BusinessException.class)
+                .hasMessage("Aliyun test read returned an empty response body")
+                .extracting("code").isEqualTo(502);
+    }
+
+    private static Body validate(Body body) {
+        return AliyunResponseValidator.requireReadableBody("test read", body,
+                Body::success, Body::code, Body::message);
+    }
+
+    private record Body(Boolean success, String code, String message) {
+    }
+}
diff --git a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogServiceTest.java b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogServiceTest.java
index b0ce00c28..ab82d0355 100644
--- a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogServiceTest.java
+++ b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunCatalogServiceTest.java
@@ -64,7 +64,7 @@ class AliyunCatalogServiceTest {
     void listRegionsShouldSkipNullSdkRecords() {
         ListRegionsResponse response = ListRegionsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListRegionsResponseBody.builder()
+                .body(ListRegionsResponseBody.builder().success(true)
                         .data(Arrays.asList(null, ListRegionsResponseBody.Data.builder()
                                 .regionId("cn-hangzhou")
                                 .supportRocketmqV5(true)
@@ -83,7 +83,7 @@ class AliyunCatalogServiceTest {
     void listRegionsShouldKeepOnlyRocketmqV5RegionsTest() {
         ListRegionsResponse response = ListRegionsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListRegionsResponseBody.builder()
+                .body(ListRegionsResponseBody.builder().success(true)
                         .data(List.of(
                                 ListRegionsResponseBody.Data.builder()
                                         .regionId("cn-shanghai").regionName("shanghai")
@@ -191,7 +191,7 @@ class AliyunCatalogServiceTest {
     void getCloudInstanceShouldMapEndpointsTest() {
         GetInstanceResponse response = GetInstanceResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetInstanceResponseBody.builder()
+                .body(GetInstanceResponseBody.builder().success(true)
                         .data(GetInstanceResponseBody.Data.builder()
                                 .instanceId("rmq-cn-001")
                                 .instanceName("prod")
@@ -227,7 +227,8 @@ class AliyunCatalogServiceTest {
 
     @Test
     void getCloudInstanceShouldNormalizeLookupIdentifiersTest() {
-        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(null);
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(
+                GetInstanceResponse.create().toBuilder().body(GetInstanceResponseBody.builder().build()).build());
 
         assertThatThrownBy(() -> service.getCloudInstance(
                 CREDENTIAL_ID, "  cn-hangzhou  ", "  rmq-missing  "))
@@ -236,6 +237,46 @@ class AliyunCatalogServiceTest {
         verify(clientFactory).call(eq(CREDENTIAL_ID), eq(REGION), any());
     }
 
+    @Test
+    void listRegionsShouldRejectUnsuccessfulBusinessResponseTest() {
+        ListRegionsResponse response = ListRegionsResponse.create().toBuilder()
+                .statusCode(200)
+                .body(ListRegionsResponseBody.builder().success(false)
+                        .code("OperationDenied").message("denied").build())
+                .build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(AliyunCatalogService.DEFAULT_REGION), any()))
+                .thenReturn(response);
+
+        assertThatThrownBy(() -> service.listRegions(CREDENTIAL_ID))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void listCloudInstancesShouldRejectUnsuccessfulBusinessResponseTest() {
+        ListInstancesResponse response = ListInstancesResponse.create().toBuilder()
+                .statusCode(200)
+                .body(ListInstancesResponseBody.builder().success(false)
+                        .code("OperationDenied").message("denied").build())
+                .build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID, REGION, null))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void getCloudInstanceShouldRejectUnsuccessfulBusinessResponseTest() {
+        GetInstanceResponse response = GetInstanceResponse.create().toBuilder()
+                .statusCode(200)
+                .body(GetInstanceResponseBody.builder().success(false)
+                        .code("OperationDenied").message("denied").build())
+                .build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.getCloudInstance(CREDENTIAL_ID, REGION, "rmq-cn-001"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
     private static List<ListInstancesResponseBody.List> instanceRows(int count, int idOffset) {
         List<ListInstancesResponseBody.List> rows = new ArrayList<>();
         for (int i = 0; i < count; i++) {
@@ -263,7 +304,7 @@ class AliyunCatalogServiceTest {
     private static ListInstancesResponse instancesResponse(List<ListInstancesResponseBody.List> rows, long totalCount) {
         return ListInstancesResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListInstancesResponseBody.builder()
+                .body(ListInstancesResponseBody.builder().success(true)
                         .data(ListInstancesResponseBody.Data.builder()
                                 .list(rows)
                                 .pageNumber(1L)
@@ -273,4 +314,51 @@ class AliyunCatalogServiceTest {
                         .build())
                 .build();
     }
+    @Test
+    void listCloudInstancesShouldTolerateOmittedSuccessFlagTest() {
+        ListInstancesResponse fixture = instancesResponse(List.of(instanceRow("rmq-a", "A")));
+        ListInstancesResponse response = ListInstancesResponse.create().toBuilder()
+                .body(ListInstancesResponseBody.builder().data(fixture.getBody().getData()).build()).build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(response);
+        assertThat(service.listCloudInstances(CREDENTIAL_ID, REGION, null)).singleElement()
+                .extracting(CloudInstanceOptionVO::getInstanceId).isEqualTo("rmq-a");
+    }
+
+    @Test
+    void listRegionsShouldTolerateOmittedSuccessFlagTest() {
+        ListRegionsResponse response = ListRegionsResponse.create().toBuilder()
+                .body(ListRegionsResponseBody.builder().data(List.of(ListRegionsResponseBody.Data.builder()
+                        .regionId(REGION).supportRocketmqV5(true).build())).build()).build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(AliyunCatalogService.DEFAULT_REGION), any()))
+                .thenReturn(response);
+        assertThat(service.listRegions(CREDENTIAL_ID)).singleElement()
+                .extracting(CloudRegionVO::getRegionId).isEqualTo(REGION);
+    }
+
+    @Test
+    void getCloudInstanceShouldTolerateOmittedSuccessFlagTest() {
+        GetInstanceResponse response = GetInstanceResponse.create().toBuilder()
+                .body(GetInstanceResponseBody.builder().data(GetInstanceResponseBody.Data.builder()
+                        .instanceId("rmq-a").instanceName("A").regionId(REGION).build()).build()).build();
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(response);
+        assertThat(service.getCloudInstance(CREDENTIAL_ID, REGION, "rmq-a").getInstanceId()).isEqualTo("rmq-a");
+    }
+
+    @org.junit.jupiter.params.ParameterizedTest
+    @org.junit.jupiter.params.provider.ValueSource(booleans = {true, false})
+    void catalogReadsShouldRejectMissingBodiesConsistentlyTest(boolean nullResponse) {
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(nullResponse ? null
+                : ListRegionsResponse.create().toBuilder().build());
+        assertThatThrownBy(() -> service.listRegions(CREDENTIAL_ID))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(nullResponse ? null
+                : ListInstancesResponse.create().toBuilder().build());
+        assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID, REGION, null))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
+        when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION), any())).thenReturn(nullResponse ? null
+                : GetInstanceResponse.create().toBuilder().build());
+        assertThatThrownBy(() -> service.getCloudInstance(CREDENTIAL_ID, REGION, "rmq-a"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
+    }
+
 }
diff --git a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
index 70a0a42fa..865aa31f9 100644
--- a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
+++ b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
@@ -28,6 +28,10 @@ import com.aliyun.sdk.service.rocketmq20220801.models.GetTraceResponseBody;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListConsumerGroupsRequest;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListConsumerGroupsResponse;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListConsumerGroupsResponseBody;
+import com.aliyun.sdk.service.rocketmq20220801.models.ListConsumerGroupSubscriptionsResponse;
+import com.aliyun.sdk.service.rocketmq20220801.models.ListConsumerGroupSubscriptionsResponseBody;
+import com.aliyun.sdk.service.rocketmq20220801.models.ListTopicSubscriptionsResponse;
+import com.aliyun.sdk.service.rocketmq20220801.models.ListTopicSubscriptionsResponseBody;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListMessagesRequest;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListMessagesResponse;
 import com.aliyun.sdk.service.rocketmq20220801.models.ListMessagesResponseBody;
@@ -169,6 +173,30 @@ class AliyunInstanceProviderTest {
                 .hasMessageContaining("send denied");
     }
 
+    @Test
+    void topicReadsShouldTolerateOmittedSuccessFlagTest() {
+        stubInstance();
+        stubCallThrough();
+        ListTopicsResponse fixture = topicsResponse(topicRow("orders", "NORMAL"));
+        ListTopicsResponse response = ListTopicsResponse.create().toBuilder()
+                .body(ListTopicsResponseBody.builder().data(fixture.getBody().getData()).build()).build();
+        when(asyncClient.listTopics(any())).thenReturn(CompletableFuture.completedFuture(response));
+        assertThat(provider.listTopics(STUDIO_INSTANCE_ID, null, null)).singleElement()
+                .extracting(TopicVO::getName).isEqualTo("orders");
+        assertThat(provider.countTopics(STUDIO_INSTANCE_ID)).isEqualTo(1);
+    }
+
+    @org.junit.jupiter.params.ParameterizedTest
+    @org.junit.jupiter.params.provider.ValueSource(booleans = {false, true})
+    void topicReadsShouldRejectMissingResponseBodyTest(boolean nullResponse) {
+        stubInstance();
+        stubCallThrough();
+        ListTopicsResponse response = nullResponse ? null : ListTopicsResponse.create().toBuilder().build();
+        when(asyncClient.listTopics(any())).thenReturn(CompletableFuture.completedFuture(response));
+        assertThatThrownBy(() -> provider.countTopics(STUDIO_INSTANCE_ID))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
+    }
+
     @Test
     void consumeMessageDirectlyShouldCallAliyunVerifyApiTest() {
         stubInstance();
@@ -348,7 +376,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         ListConsumerGroupsResponse response = ListConsumerGroupsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListConsumerGroupsResponseBody.builder()
+                .body(ListConsumerGroupsResponseBody.builder().success(true)
                         .data(ListConsumerGroupsResponseBody.Data.builder()
                                 .list(java.util.Arrays.asList(null,
                                         ListConsumerGroupsResponseBody.List.builder()
@@ -413,7 +441,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         ListConsumerGroupsResponse response = ListConsumerGroupsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListConsumerGroupsResponseBody.builder()
+                .body(ListConsumerGroupsResponseBody.builder().success(true)
                         .data(ListConsumerGroupsResponseBody.Data.builder()
                                 .list(java.util.Arrays.asList(ListConsumerGroupsResponseBody.List.builder()
                                         .consumerGroupId("GID_plain")
@@ -510,7 +538,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetConsumerGroupLagResponse response = GetConsumerGroupLagResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetConsumerGroupLagResponseBody.builder()
+                .body(GetConsumerGroupLagResponseBody.builder().success(true)
                         .data(GetConsumerGroupLagResponseBody.Data.builder()
                                 .consumerGroupId("GID_test")
                                 .topicLagMap(Map.of("topic-a",
@@ -543,7 +571,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetConsumerGroupLagResponse response = GetConsumerGroupLagResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetConsumerGroupLagResponseBody.builder()
+                .body(GetConsumerGroupLagResponseBody.builder().success(true)
                         .data(GetConsumerGroupLagResponseBody.Data.builder()
                                 .consumerGroupId("GID_test")
                                 .totalLag(GetConsumerGroupLagResponseBody.TotalLag.builder()
@@ -569,7 +597,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetConsumerGroupLagResponse response = GetConsumerGroupLagResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetConsumerGroupLagResponseBody.builder()
+                .body(GetConsumerGroupLagResponseBody.builder().success(true)
                         .data(GetConsumerGroupLagResponseBody.Data.builder()
                                 .consumerGroupId("GID_test")
                                 .topicLagMap(Map.of("topic-a",
@@ -601,7 +629,7 @@ class AliyunInstanceProviderTest {
         String encodedBody = Base64.getEncoder().encodeToString("hello".getBytes(StandardCharsets.UTF_8));
         ListMessagesResponse response = ListMessagesResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListMessagesResponseBody.builder()
+                .body(ListMessagesResponseBody.builder().success(true)
                         .data(ListMessagesResponseBody.Data.builder()
                                 .list(java.util.Arrays.asList(
                                         null,
@@ -816,7 +844,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetTraceResponse response = GetTraceResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetTraceResponseBody.builder()
+                .body(GetTraceResponseBody.builder().success(true)
                         .data(GetTraceResponseBody.Data.builder()
                                 .producerInfo(GetTraceResponseBody.ProducerInfo.builder()
                                         .records(List.of(GetTraceResponseBody.ProducerInfoRecords.builder()
@@ -880,7 +908,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetTraceResponse response = GetTraceResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetTraceResponseBody.builder()
+                .body(GetTraceResponseBody.builder().success(true)
                         .data(GetTraceResponseBody.Data.builder()
                                 .consumerInfos(List.of(GetTraceResponseBody.ConsumerInfos.builder()
                                         .consumerGroupId("GID_test")
@@ -915,7 +943,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         GetTraceResponse response = GetTraceResponse.create().toBuilder()
                 .statusCode(200)
-                .body(GetTraceResponseBody.builder().data(null).build())
+                .body(GetTraceResponseBody.builder().success(true).data(null).build())
                 .build();
         when(asyncClient.getTrace(any())).thenReturn(CompletableFuture.completedFuture(response));
 
@@ -926,7 +954,7 @@ class AliyunInstanceProviderTest {
     }
 
     @Test
-    void getMessageTraceShouldReturnEmptyTraceWhenAliyunBodyIsNullTest() {
+    void getMessageTraceShouldRejectMissingAliyunBodyTest() {
         stubInstance();
         stubCallThrough();
         GetTraceResponse response = GetTraceResponse.create().toBuilder()
@@ -935,22 +963,18 @@ class AliyunInstanceProviderTest {
                 .build();
         when(asyncClient.getTrace(any())).thenReturn(CompletableFuture.completedFuture(response));
 
-        TraceRecordVO trace = provider.getMessageTrace(STUDIO_INSTANCE_ID, "msg-without-body", "orders");
-
-        assertThat(trace.getNodes()).isEmpty();
-        assertThat(trace.getConsumerStatus()).isEmpty();
+        assertThatThrownBy(() -> provider.getMessageTrace(STUDIO_INSTANCE_ID, "msg-without-body", "orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
     }
 
     @Test
-    void getMessageTraceShouldReturnEmptyTraceWhenAliyunResponseIsNullTest() {
+    void getMessageTraceShouldRejectMissingAliyunResponseTest() {
         stubInstance();
         stubCallThrough();
         when(asyncClient.getTrace(any())).thenReturn(CompletableFuture.completedFuture(null));
 
-        TraceRecordVO trace = provider.getMessageTrace(STUDIO_INSTANCE_ID, "msg-without-response", "orders");
-
-        assertThat(trace.getNodes()).isEmpty();
-        assertThat(trace.getConsumerStatus()).isEmpty();
+        assertThatThrownBy(() -> provider.getMessageTrace(STUDIO_INSTANCE_ID, "msg-without-response", "orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(502);
     }
 
     @Test
@@ -981,6 +1005,123 @@ class AliyunInstanceProviderTest {
                 .isEqualTo(400);
     }
 
+    @Test
+    void countTopicsShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listTopics(any())).thenReturn(CompletableFuture.completedFuture(
+                ListTopicsResponse.create().toBuilder().statusCode(200)
+                        .body(ListTopicsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.countTopics(STUDIO_INSTANCE_ID))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void listTopicsShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listTopics(any())).thenReturn(CompletableFuture.completedFuture(
+                ListTopicsResponse.create().toBuilder().statusCode(200)
+                        .body(ListTopicsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.listTopics(STUDIO_INSTANCE_ID, null, null))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void countGroupsShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listConsumerGroups(any())).thenReturn(CompletableFuture.completedFuture(
+                ListConsumerGroupsResponse.create().toBuilder().statusCode(200)
+                        .body(ListConsumerGroupsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.countGroups(STUDIO_INSTANCE_ID))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void listConsumerGroupsShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listConsumerGroups(any())).thenReturn(CompletableFuture.completedFuture(
+                ListConsumerGroupsResponse.create().toBuilder().statusCode(200)
+                        .body(ListConsumerGroupsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.listConsumerGroups(STUDIO_INSTANCE_ID, null))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void getTopicConsumersShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listTopicSubscriptions(any())).thenReturn(CompletableFuture.completedFuture(
+                ListTopicSubscriptionsResponse.create().toBuilder().statusCode(200)
+                        .body(ListTopicSubscriptionsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.getTopicConsumers(STUDIO_INSTANCE_ID, "orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void getGroupProgressShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.getConsumerGroupLag(any())).thenReturn(CompletableFuture.completedFuture(
+                GetConsumerGroupLagResponse.create().toBuilder().statusCode(200)
+                        .body(GetConsumerGroupLagResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.getGroupProgress(STUDIO_INSTANCE_ID, "GID_orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void getGroupSubscriptionsShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listConsumerGroupSubscriptions(any())).thenReturn(CompletableFuture.completedFuture(
+                ListConsumerGroupSubscriptionsResponse.create().toBuilder().statusCode(200)
+                        .body(ListConsumerGroupSubscriptionsResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.getGroupSubscriptions(STUDIO_INSTANCE_ID, "GID_orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void queryMessagesShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.listMessages(any())).thenReturn(CompletableFuture.completedFuture(
+                ListMessagesResponse.create().toBuilder().statusCode(200)
+                        .body(ListMessagesResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.queryMessages(STUDIO_INSTANCE_ID, "orders", null, null, null, null, null))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
+    @Test
+    void getMessageTraceShouldRejectUnsuccessfulBusinessResponseTest() {
+        stubInstance();
+        stubCallThrough();
+        when(asyncClient.getTrace(any())).thenReturn(CompletableFuture.completedFuture(
+                GetTraceResponse.create().toBuilder().statusCode(200)
+                        .body(GetTraceResponseBody.builder().success(false)
+                                .code("OperationDenied").message("denied").build()).build()));
+
+        assertThatThrownBy(() -> provider.getMessageTrace(STUDIO_INSTANCE_ID, "msg-1", "orders"))
+                .isInstanceOf(BusinessException.class).extracting("code").isEqualTo(422);
+    }
+
     private void stubInstance() {
         InstanceVO instance = InstanceVO.builder()
                 .name("aliyun-prod")
@@ -1017,7 +1158,7 @@ class AliyunInstanceProviderTest {
             ListTopicsResponseBody.List... rows) {
         return ListTopicsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListTopicsResponseBody.builder()
+                .body(ListTopicsResponseBody.builder().success(true)
                         .data(ListTopicsResponseBody.Data.builder()
                                 .list(java.util.Arrays.asList(rows))
                                 .pageNumber(pageNumber)
@@ -1046,7 +1187,7 @@ class AliyunInstanceProviderTest {
                 .toList();
         return ListMessagesResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListMessagesResponseBody.builder()
+                .body(ListMessagesResponseBody.builder().success(true)
                         .data(ListMessagesResponseBody.Data.builder()
                                 .list(rows)
                                 .pageNumber((long) pageNumber)
@@ -1063,7 +1204,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         ListTopicsResponse response = ListTopicsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListTopicsResponseBody.builder()
+                .body(ListTopicsResponseBody.builder().success(true)
                         .data(ListTopicsResponseBody.Data.builder()
                                 .list(List.of(topicRow("topic-a", "NORMAL")))
                                 .pageNumber(1L)
@@ -1088,7 +1229,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         ListConsumerGroupsResponse response = ListConsumerGroupsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListConsumerGroupsResponseBody.builder()
+                .body(ListConsumerGroupsResponseBody.builder().success(true)
                         .data(ListConsumerGroupsResponseBody.Data.builder()
                                 .list(List.of(ListConsumerGroupsResponseBody.List.builder()
                                         .consumerGroupId("GID_one")
@@ -1116,7 +1257,7 @@ class AliyunInstanceProviderTest {
         stubCallThrough();
         ListTopicsResponse missingTotal = ListTopicsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListTopicsResponseBody.builder()
+                .body(ListTopicsResponseBody.builder().success(true)
                         .data(ListTopicsResponseBody.Data.builder()
                                 .list(List.of(topicRow("topic-a", "NORMAL")))
                                 .pageNumber(1L)
@@ -1158,7 +1299,7 @@ class AliyunInstanceProviderTest {
     private static ListConsumerGroupsResponse groupsResponse(Long totalCount, String... groupIds) {
         return ListConsumerGroupsResponse.create().toBuilder()
                 .statusCode(200)
-                .body(ListConsumerGroupsResponseBody.builder()
+                .body(ListConsumerGroupsResponseBody.builder().success(true)
                         .data(ListConsumerGroupsResponseBody.Data.builder()
                                 .list(java.util.Arrays.stream(groupIds)
                                         .map(groupId -> ListConsumerGroupsResponseBody.List.builder()