diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index f2a43a8693..4774c78316 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -741,6 +741,7 @@
"delete": "Delete",
"delete_confirm": "Are you sure you want to delete?",
"description": "Description",
+ "detail": "Detail",
"disabled": "Disabled",
"docs": "Docs",
"download": "Download",
@@ -825,6 +826,8 @@
},
"response": "Something went wrong. Please check if you have set your API key in the Settings > Providers"
},
+ "detail": "Error Details",
+ "details": "Details",
"http": {
"400": "Request failed. Please check if the request parameters are correct. If you have changed the model settings, please reset them to the default settings",
"401": "Authentication failed. Please check if your API key is correct",
@@ -836,6 +839,7 @@
"503": "Service unavailable. Please try again later",
"504": "Gateway timeout. Please try again later"
},
+ "message": "Error Message",
"missing_user_message": "Cannot switch model response: The original user message has been deleted. Please send a new message to get a response with this model.",
"model": {
"exists": "Model already exists",
@@ -848,6 +852,10 @@
"description": "Failed to render message content. Please check if the message content format is correct",
"title": "Render Error"
},
+ "requestBody": "Request Body",
+ "requestUrl": "Request URL",
+ "stack": "Stack Trace",
+ "status": "Status Code",
"unknown": "Unknown error",
"user_message_not_found": "Cannot find original user message to resend"
},
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index ad22c06fe6..0666f7868f 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -741,6 +741,7 @@
"delete": "削除",
"delete_confirm": "削除してもよろしいですか?",
"description": "説明",
+ "detail": "詳細",
"disabled": "無効",
"docs": "ドキュメント",
"download": "ダウンロード",
@@ -825,6 +826,8 @@
},
"response": "エラーが発生しました。APIキーが設定されていない場合は、設定 > プロバイダーでキーを設定してください"
},
+ "detail": "エラーの詳細",
+ "details": "詳細",
"http": {
"400": "リクエストに失敗しました。リクエストパラメータが正しいか確認してください。モデルの設定を変更した場合は、デフォルトの設定にリセットしてください",
"401": "認証に失敗しました。APIキーが正しいか確認してください",
@@ -836,6 +839,7 @@
"503": "サービスが利用できません。後でもう一度試してください",
"504": "ゲートウェイタイムアウトが発生しました。後でもう一度試してください"
},
+ "message": "エラーメッセージ",
"missing_user_message": "モデル応答を切り替えられません:元のユーザーメッセージが削除されました。このモデルで応答を得るには、新しいメッセージを送信してください",
"model": {
"exists": "モデルが既に存在します",
@@ -848,6 +852,9 @@
"description": "メッセージの内容のレンダリングに失敗しました。メッセージの内容の形式が正しいか確認してください",
"title": "レンダリングエラー"
},
+ "requestBody": "要求されたコンテンツ",
+ "stack": "スタック情報",
+ "status": "ステータスコード",
"unknown": "不明なエラー",
"user_message_not_found": "元のユーザーメッセージを見つけることができませんでした"
},
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index c231102a23..0c4d9a426d 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -741,6 +741,7 @@
"delete": "Удалить",
"delete_confirm": "Вы уверены, что хотите удалить?",
"description": "Описание",
+ "detail": "Подробности",
"disabled": "Отключено",
"docs": "Документы",
"download": "Скачать",
@@ -825,6 +826,8 @@
},
"response": "Что-то пошло не так. Пожалуйста, проверьте, установлен ли ваш ключ API в Настройки > Провайдеры"
},
+ "detail": "Детали ошибки",
+ "details": "Подробности",
"http": {
"400": "Не удалось выполнить запрос. Пожалуйста, проверьте, правильно ли настроены параметры запроса. Если вы изменили настройки модели, пожалуйста, сбросьте их до значений по умолчанию",
"401": "Не удалось пройти аутентификацию. Пожалуйста, проверьте, правильно ли настроен ваш ключ API",
@@ -836,6 +839,7 @@
"503": "Серверная ошибка. Пожалуйста, попробуйте позже",
"504": "Серверная ошибка. Пожалуйста, попробуйте позже"
},
+ "message": "Сообщение об ошибке",
"missing_user_message": "Невозможно изменить модель ответа: исходное сообщение пользователя было удалено. Пожалуйста, отправьте новое сообщение, чтобы получить ответ от этой модели",
"model": {
"exists": "Модель уже существует",
@@ -848,6 +852,9 @@
"description": "Не удалось рендерить содержимое сообщения. Пожалуйста, проверьте, правильно ли формат содержимого сообщения",
"title": "Ошибка рендеринга"
},
+ "requestBody": "Запрашиваемый контент",
+ "stack": "Информация стека",
+ "status": "Код статуса",
"unknown": "Неизвестная ошибка",
"user_message_not_found": "Не удалось найти исходное сообщение пользователя"
},
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 81b7173845..5f8d13c02d 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -741,6 +741,7 @@
"delete": "删除",
"delete_confirm": "确定要删除吗?",
"description": "描述",
+ "detail": "详情",
"disabled": "已禁用",
"docs": "文档",
"download": "下载",
@@ -825,6 +826,8 @@
},
"response": "出错了,如果没有配置 API 密钥,请前往设置 > 模型提供商中配置密钥"
},
+ "detail": "错误详情",
+ "details": "详细信息",
"http": {
"400": "请求错误,请检查请求参数是否正确。如果修改了模型设置,请重置到默认设置",
"401": "身份验证失败,请检查 API 密钥是否正确",
@@ -836,6 +839,7 @@
"503": "服务不可用,请稍后再试",
"504": "网关超时,请稍后再试"
},
+ "message": "错误信息",
"missing_user_message": "无法切换模型响应:原始用户消息已被删除。请发送新消息以获取此模型的响应",
"model": {
"exists": "模型已存在",
@@ -848,6 +852,10 @@
"description": "消息内容渲染失败,请检查消息内容格式是否正确",
"title": "渲染错误"
},
+ "requestBody": "请求内容",
+ "requestUrl": "请求路径",
+ "stack": "堆栈信息",
+ "status": "状态码",
"unknown": "未知错误",
"user_message_not_found": "无法找到原始用户消息"
},
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index 195f1b5276..4facd5557b 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -741,6 +741,7 @@
"delete": "刪除",
"delete_confirm": "確定要刪除嗎?",
"description": "描述",
+ "detail": "詳情",
"disabled": "已停用",
"docs": "文件",
"download": "下載",
@@ -825,6 +826,8 @@
},
"response": "出現錯誤。如果尚未設定 API 金鑰,請前往設定 > 模型提供者中設定金鑰"
},
+ "detail": "錯誤詳情",
+ "details": "詳細信息",
"http": {
"400": "請求錯誤,請檢查請求參數是否正確。如果修改了模型設定,請重設到預設設定",
"401": "身份驗證失敗,請檢查 API 金鑰是否正確",
@@ -836,6 +839,7 @@
"503": "服務無法使用,請稍後再試",
"504": "閘道器超時,請稍後再試"
},
+ "message": "錯誤訊息",
"missing_user_message": "無法切換模型回應:原始用戶訊息已被刪除。請發送新訊息以獲得此模型回應。",
"model": {
"exists": "模型已存在",
@@ -848,6 +852,9 @@
"description": "消息內容渲染失敗,請檢查消息內容格式是否正確",
"title": "渲染錯誤"
},
+ "requestBody": "請求內容",
+ "stack": "堆棧信息",
+ "status": "狀態碼",
"unknown": "未知錯誤",
"user_message_not_found": "無法找到原始用戶訊息"
},
diff --git a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx
index 38f687e6d2..58dbd7e7d4 100644
--- a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx
+++ b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx
@@ -1,10 +1,11 @@
+import CodeViewer from '@renderer/components/CodeViewer'
import { useTimer } from '@renderer/hooks/useTimer'
import { getHttpMessageLabel } from '@renderer/i18n/label'
import { useAppDispatch } from '@renderer/store'
import { removeBlocksThunk } from '@renderer/store/thunk/messageThunk'
import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
-import { Alert as AntdAlert } from 'antd'
-import React from 'react'
+import { Alert as AntdAlert, Button, Modal } from 'antd'
+import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@@ -21,6 +22,7 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock; message: Message }>
const { t, i18n } = useTranslation()
const dispatch = useAppDispatch()
const { setTimeoutTimer } = useTimer()
+ const [showDetailModal, setShowDetailModal] = useState(false)
const HTTP_ERROR_CODES = [400, 401, 403, 404, 429, 500, 502, 503, 504]
@@ -28,27 +30,225 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock; message: Message }>
setTimeoutTimer('onRemoveBlock', () => dispatch(removeBlocksThunk(message.topicId, message.id, [block.id])), 350)
}
+ const showErrorDetail = () => {
+ setShowDetailModal(true)
+ }
+
if (block.error && HTTP_ERROR_CODES.includes(block.error?.status)) {
return (
-
+ <>
+ {
+ e.stopPropagation()
+ showErrorDetail()
+ }}>
+ {t('common.detail')}
+
+ }
+ />
+ setShowDetailModal(false)} error={block.error} />
+ >
)
}
if (block?.error?.message) {
const errorKey = `error.${block.error.message}`
const pauseErrorLanguagePlaceholder = i18n.exists(errorKey) ? t(errorKey) : block.error.message
- return
+ return (
+ <>
+ {
+ e.stopPropagation()
+ showErrorDetail()
+ }}>
+ {t('common.detail')}
+
+ }
+ />
+ setShowDetailModal(false)} error={block.error} />
+ >
+ )
}
- return
+ return (
+ <>
+ {
+ e.stopPropagation()
+ showErrorDetail()
+ }}>
+ {t('common.detail')}
+
+ }
+ />
+ setShowDetailModal(false)} error={block.error} />
+ >
+ )
}
+interface ErrorDetailModalProps {
+ open: boolean
+ onClose: () => void
+ error?: Record
+}
+
+const ErrorDetailModal: React.FC = ({ open, onClose, error }) => {
+ const { t } = useTranslation()
+
+ const copyErrorDetails = () => {
+ if (!error) return
+
+ const errorText = `
+${t('error.message')}: ${error.message || 'N/A'}
+${t('error.requestUrl')}: ${error.url || 'N/A'}
+${t('error.requestBody')}: ${error.requestBody ? JSON.stringify(error.requestBody, null, 2) : 'N/A'}
+${t('error.stack')}: ${error.stack || 'N/A'}
+ `.trim()
+
+ navigator.clipboard.writeText(errorText)
+ }
+
+ const renderErrorDetails = (error: any) => {
+ if (!error) return {t('error.unknown')}
+
+ return (
+
+ {error.message && (
+
+ {t('error.message')}:
+ {error.message}
+
+ )}
+
+ {error.url && (
+
+ {t('error.requestUrl')}:
+ {error.url}
+
+ )}
+
+ {error.requestBody && (
+
+ {t('error.requestBody')}:
+
+ {JSON.stringify(error.requestBody, null, 2)}
+
+
+ )}
+
+ {error.stack && (
+
+ {t('error.stack')}:
+
+ {error.stack}
+
+
+ )}
+
+ )
+ }
+
+ return (
+
+ {t('common.copy')}
+ ,
+
+ ]}
+ width={600}>
+ {renderErrorDetails(error)}
+
+ )
+}
+
+const ErrorDetailContainer = styled.div`
+ max-height: 400px;
+ overflow-y: auto;
+`
+
+const ErrorDetailList = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+`
+
+const ErrorDetailItem = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+`
+
+const ErrorDetailLabel = styled.div`
+ font-weight: 600;
+ color: var(--color-text);
+ font-size: 14px;
+`
+
+const ErrorDetailValue = styled.div`
+ font-family: var(--code-font-family);
+ font-size: 12px;
+ padding: 8px;
+ background: var(--color-code-background);
+ border-radius: 4px;
+ border: 1px solid var(--color-border);
+ word-break: break-word;
+ color: var(--color-text);
+`
+
+const StackTrace = styled.div`
+ background: var(--color-background-soft);
+ border: 1px solid var(--color-error);
+ border-radius: 6px;
+ padding: 12px;
+
+ pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-break: break-word;
+ font-family: var(--code-font-family);
+ font-size: 12px;
+ line-height: 1.4;
+ color: var(--color-error);
+ }
+`
+
const Alert = styled(AntdAlert)`
margin: 0.5rem 0 !important;
padding: 10px;
diff --git a/src/renderer/src/services/messageStreaming/callbacks/baseCallbacks.ts b/src/renderer/src/services/messageStreaming/callbacks/baseCallbacks.ts
index d4fe37e947..2c7ac234a8 100644
--- a/src/renderer/src/services/messageStreaming/callbacks/baseCallbacks.ts
+++ b/src/renderer/src/services/messageStreaming/callbacks/baseCallbacks.ts
@@ -19,7 +19,7 @@ import { isAbortError, serializeError } from '@renderer/utils/error'
import { createBaseMessageBlock, createErrorBlock } from '@renderer/utils/messageUtils/create'
import { findAllBlocks, getMainTextContent } from '@renderer/utils/messageUtils/find'
import { isFocused, isOnHomePage } from '@renderer/utils/window'
-import { AISDKError } from 'ai'
+import { AISDKError, NoOutputGeneratedError } from 'ai'
import { BlockManager } from '../BlockManager'
@@ -82,6 +82,9 @@ export const createBaseCallbacks = (deps: BaseCallbacksDependencies) => {
onError: async (error: AISDKError) => {
logger.debug('onError', error)
+ if (NoOutputGeneratedError.isInstance(error)) {
+ return
+ }
const isErrorTypeAbort = isAbortError(error)
const serializableError = serializeError(error)
if (isErrorTypeAbort) {
diff --git a/src/renderer/src/utils/error.ts b/src/renderer/src/utils/error.ts
index 54639a333a..d617124582 100644
--- a/src/renderer/src/utils/error.ts
+++ b/src/renderer/src/utils/error.ts
@@ -103,7 +103,13 @@ export const serializeError = (error: AISDKError) => {
} catch (e: any) {
logger.warn('Error parsing error response body:', e)
}
- return { ...baseError, status: error.statusCode, url: error.url, message: content }
+ return {
+ ...baseError,
+ status: error.statusCode,
+ url: error.url,
+ message: content,
+ requestBody: error.requestBodyValues
+ }
}
return baseError
}