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 1e8cc5919 fix(instance): surface the server error reason when creating 
or updating an instance (#4241)
1e8cc5919 is described below

commit 1e8cc5919aa5d110f59af60361ffc3a9c4154f9c
Author: 烤化の初雪 <[email protected]>
AuthorDate: Tue Sep 15 21:13:32 2026 +0800

    fix(instance): surface the server error reason when creating or updating an 
instance (#4241)
    
    (cherry picked from commit efafdda638cb2bd335eccb2eff2b574dd6633490)
    
    Co-authored-by: unbridled-41 
<[email protected]>
---
 .../pages/instance/__tests__/InstancePage.test.tsx | 45 ++++++++++++++++++++++
 web/src/pages/instance/index.tsx                   |  4 +-
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/web/src/pages/instance/__tests__/InstancePage.test.tsx 
b/web/src/pages/instance/__tests__/InstancePage.test.tsx
index 2e1cc2b7b..065b3ac90 100644
--- a/web/src/pages/instance/__tests__/InstancePage.test.tsx
+++ b/web/src/pages/instance/__tests__/InstancePage.test.tsx
@@ -440,6 +440,51 @@ describe('InstancePage', () => {
     expect(screen.queryByText('请输入接入地址')).not.toBeInTheDocument();
   });
 
+  it('surfaces the server error reason when creating an instance fails', async 
() => {
+    const user = userEvent.setup();
+    vi.mocked(instanceService.createInstance).mockRejectedValue({
+      response: { data: { message: 'Instance name already exists: new-proxy' } 
},
+    });
+    renderPage();
+
+    expect(await screen.findByText('production-proxy')).toBeInTheDocument();
+    await user.click(screen.getByRole('button', { name: /添加实例/ }));
+    const dialog = await screen.findByRole('dialog');
+    await user.type(within(dialog).getByLabelText('实例 ID'), 'new-proxy');
+    const createTypeSelect = within(dialog).getByRole('combobox');
+    fireEvent.mouseDown(createTypeSelect.parentElement!);
+    const proxyOptions = await screen.findAllByText('Proxy Cluster 模式', {
+      selector: '.ant-select-item-option-content',
+    });
+    await user.click(proxyOptions[proxyOptions.length - 1]);
+    await user.type(within(dialog).getByLabelText('接入地址'), 'proxy-new:8080');
+    await user.click(within(dialog).getByRole('button', { name: /连\s*接/ }));
+
+    expect(await screen.findByText('Instance name already exists: 
new-proxy')).toBeInTheDocument();
+    expect(screen.queryByText('添加实例失败,请稍后重试')).not.toBeInTheDocument();
+  });
+
+  it('surfaces the server error reason when updating an instance fails', async 
() => {
+    const user = userEvent.setup();
+    vi.mocked(instanceService.updateInstance).mockRejectedValue({
+      response: { data: { message: 'Instance endpoint is not reachable' } },
+    });
+    renderPage();
+
+    expect(await screen.findByText('production-proxy')).toBeInTheDocument();
+    const row = screen.getByRole('row', { name: /production-proxy/ });
+    await user.click(within(row).getByRole('button', { name: /编\s*辑/ }));
+    const dialog = await screen.findByRole('dialog');
+
+    const endpointInput = within(dialog).getByLabelText('接入地址');
+    await user.clear(endpointInput);
+    await user.type(endpointInput, 'namesrv-new:9876');
+    await user.click(within(dialog).getByRole('button', { name: /保\s*存/ }));
+
+    expect(await screen.findByText('Instance endpoint is not 
reachable')).toBeInTheDocument();
+    expect(screen.queryByText('更新实例失败,请稍后重试')).not.toBeInTheDocument();
+  });
+
   it('reloads the latest filters after a pending instance deletion completes', 
async () => {
     const user = userEvent.setup();
     const pendingDelete = deferred<void>();
diff --git a/web/src/pages/instance/index.tsx b/web/src/pages/instance/index.tsx
index 23eb74185..146013c65 100644
--- a/web/src/pages/instance/index.tsx
+++ b/web/src/pages/instance/index.tsx
@@ -375,7 +375,7 @@ const InstancePage = () => {
       if (error && typeof error === 'object' && 'errorFields' in error) {
         return; // validation failure; antd already shows field-level errors
       }
-      message.error(t('instance.createFailed'));
+      message.error(describeApiError(error, t('instance.createFailed')));
     } finally {
       mutationInFlightRef.current = false;
       setSubmitting(false);
@@ -459,7 +459,7 @@ const InstancePage = () => {
       if (error && typeof error === 'object' && 'errorFields' in error) {
         return; // validation failure; antd already shows field-level errors
       }
-      message.error(t('instance.updateFailed'));
+      message.error(describeApiError(error, t('instance.updateFailed')));
     } finally {
       mutationInFlightRef.current = false;
       setSubmitting(false);

Reply via email to