From e6c18022a511b795f6d5d588dac72519b43be037 Mon Sep 17 00:00:00 2001 From: Haze <709547807@qq.com> Date: Thu, 30 Apr 2026 18:46:55 +0800 Subject: [PATCH] feat(chat): add localized error message for model API call and improve error handling in chat history (#947) --- src/i18n/locales/en/chat.json | 3 +++ src/i18n/locales/ja/chat.json | 3 +++ src/i18n/locales/ru/chat.json | 3 +++ src/i18n/locales/zh/chat.json | 3 +++ src/pages/Chat/index.tsx | 2 +- tests/e2e/chat-task-visualizer.spec.ts | 3 +++ tests/unit/chat-history-actions.test.ts | 31 +++++++++++++++++++++++++ 7 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/i18n/locales/en/chat.json b/src/i18n/locales/en/chat.json index 4df8603..19bf24f 100644 --- a/src/i18n/locales/en/chat.json +++ b/src/i18n/locales/en/chat.json @@ -1,4 +1,7 @@ { + "runError": { + "title": "Model API call error" + }, "gatewayNotRunning": "Gateway Not Running", "gatewayRequired": "The OpenClaw Gateway needs to be running to use chat. It will start automatically, or you can start it from Settings.", "welcome": { diff --git a/src/i18n/locales/ja/chat.json b/src/i18n/locales/ja/chat.json index 5b56b37..adeda41 100644 --- a/src/i18n/locales/ja/chat.json +++ b/src/i18n/locales/ja/chat.json @@ -1,4 +1,7 @@ { + "runError": { + "title": "モデルAPI呼び出しエラー" + }, "gatewayNotRunning": "ゲートウェイが停止中", "gatewayRequired": "チャットを利用するには OpenClaw ゲートウェイが実行されている必要があります。自動的に起動するか、設定から起動できます。", "welcome": { diff --git a/src/i18n/locales/ru/chat.json b/src/i18n/locales/ru/chat.json index 05fda6f..3851fe1 100644 --- a/src/i18n/locales/ru/chat.json +++ b/src/i18n/locales/ru/chat.json @@ -1,4 +1,7 @@ { + "runError": { + "title": "Ошибка вызова API модели" + }, "gatewayNotRunning": "Шлюз не запущен", "gatewayRequired": "Для использования чата требуется запущенный шлюз OpenClaw. Он запустится автоматически, или вы можете запустить его в Настройках.", "welcome": { diff --git a/src/i18n/locales/zh/chat.json b/src/i18n/locales/zh/chat.json index 3e37e46..87fbfd0 100644 --- a/src/i18n/locales/zh/chat.json +++ b/src/i18n/locales/zh/chat.json @@ -1,4 +1,7 @@ { + "runError": { + "title": "模型API调用错误" + }, "gatewayNotRunning": "网关未运行", "gatewayRequired": "OpenClaw 网关需要运行才能使用聊天。它将自动启动,或者您可以从设置中启动。", "welcome": { diff --git a/src/pages/Chat/index.tsx b/src/pages/Chat/index.tsx index ee02b44..0f2f605 100644 --- a/src/pages/Chat/index.tsx +++ b/src/pages/Chat/index.tsx @@ -805,7 +805,7 @@ export function Chat() { {/* Run error callout */} {runError && ( -
+

diff --git a/tests/e2e/chat-task-visualizer.spec.ts b/tests/e2e/chat-task-visualizer.spec.ts index 62f94d0..094597a 100644 --- a/tests/e2e/chat-task-visualizer.spec.ts +++ b/tests/e2e/chat-task-visualizer.spec.ts @@ -416,6 +416,9 @@ test.describe('ClawX chat execution graph', () => { await expect(page.getByTestId('main-layout')).toBeVisible(); await expect(page.getByText('404 Resource not found')).toBeVisible({ timeout: 30_000 }); + const runErrorCallout = page.getByTestId('chat-run-error'); + await expect(runErrorCallout).toBeVisible({ timeout: 30_000 }); + await expect(runErrorCallout).toContainText('404 Resource not found'); await expect(page.getByTestId('chat-execution-graph')).toHaveCount(0); await expect(page.getByTestId('chat-execution-step-thinking-trailing')).toHaveCount(0); await expect(page.getByText('404 Resource not found')).toHaveCount(1); diff --git a/tests/unit/chat-history-actions.test.ts b/tests/unit/chat-history-actions.test.ts index 3abe6b8..21e8537 100644 --- a/tests/unit/chat-history-actions.test.ts +++ b/tests/unit/chat-history-actions.test.ts @@ -308,6 +308,37 @@ describe('chat history actions', () => { ]); }); + it('does not set runError from an older assistant failure when a later turn succeeded', async () => { + const { createHistoryActions } = await import('@/stores/chat/history-actions'); + const h = makeHarness({ + currentSessionKey: 'agent:main:main', + runError: 'stale', + }); + const actions = createHistoryActions(h.set as never, h.get as never); + + invokeIpcMock.mockResolvedValueOnce({ + success: true, + result: { + messages: [ + { role: 'user', content: 'first', timestamp: 1773281730 }, + { + role: 'assistant', + content: 'fail', + timestamp: 1773281731, + stopReason: 'error', + errorMessage: '400 bad model', + }, + { role: 'user', content: 'retry', timestamp: 1773281732 }, + { role: 'assistant', content: 'ok now', timestamp: 1773281733 }, + ], + }, + }); + + await actions.loadHistory(true); + + expect(h.read().runError).toBeNull(); + }); + it('retries the first foreground startup history load after a timeout and then succeeds', async () => { vi.useFakeTimers(); const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});