diff --git a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx index 1ed33e7475..38f687e6d2 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ErrorBlock.tsx @@ -32,7 +32,7 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock; message: Message }> return ( { // await blockManager.handleBlockTransition(baseBlock as PlaceholderMessageBlock, MessageBlockType.UNKNOWN) // }, - onError: async (error: any) => { + onError: async (error: AISDKError) => { logger.debug('onError', error) const isErrorTypeAbort = isAbortError(error) - let pauseErrorLanguagePlaceholder = '' + const serializableError = serializeError(error) if (isErrorTypeAbort) { - pauseErrorLanguagePlaceholder = 'pause_placeholder' - } - - const serializableError = { - name: error.name, - message: pauseErrorLanguagePlaceholder || error.message || formatErrorMessage(error), - originalMessage: error.message, - stack: error.stack, - status: error.status || error.code, - requestId: error.request_id + serializableError.message = 'pause_placeholder' } const duration = Date.now() - startTime diff --git a/src/renderer/src/types/newMessage.ts b/src/renderer/src/types/newMessage.ts index db7370fb13..7a33c00b81 100644 --- a/src/renderer/src/types/newMessage.ts +++ b/src/renderer/src/types/newMessage.ts @@ -50,7 +50,7 @@ export interface BaseMessageBlock { status: MessageBlockStatus // 块状态 model?: Model // 使用的模型 metadata?: Record // 通用元数据 - error?: Record // Added optional error field to base + error?: Record // Serializable error object instead of AISDKError } export interface PlaceholderMessageBlock extends BaseMessageBlock { diff --git a/src/renderer/src/utils/__tests__/error.test.ts b/src/renderer/src/utils/__tests__/error.test.ts index fa130d82bb..8ce278ce09 100644 --- a/src/renderer/src/utils/__tests__/error.test.ts +++ b/src/renderer/src/utils/__tests__/error.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' -import { formatErrorMessage, formatMessageError, getErrorDetails, getErrorMessage, isAbortError } from '../error' +import { formatErrorMessage, getErrorDetails, isAbortError } from '../error' describe('error', () => { describe('getErrorDetails', () => { @@ -124,79 +124,6 @@ describe('error', () => { }) }) - describe('formatMessageError', () => { - it('should return error details as an object', () => { - const error = new Error('Test error') - const result = formatMessageError(error) - - expect(result.message).toBe('Test error') - expect(result.stack).toBeUndefined() - expect(result.headers).toBeUndefined() - expect(result.request_id).toBeUndefined() - }) - - it('should handle string errors', () => { - const result = formatMessageError('String error') - expect(typeof result).toBe('string') - expect(result).toBe('String error') - }) - - it('should handle formatting errors', () => { - const problematicError = { - get message() { - throw new Error('Cannot access') - }, - toString: () => 'Error object' - } - - const result = formatMessageError(problematicError) - expect(result).toBeTruthy() - }) - - it('should handle completely invalid errors', () => { - let invalidError: any - try { - invalidError = Object.create(null) - Object.defineProperty(invalidError, 'toString', { - get: () => { - throw new Error() - } - }) - } catch (e) { - invalidError = { - toString() { - throw new Error() - } - } - } - - const result = formatMessageError(invalidError) - expect(result).toBeTruthy() - }) - }) - - describe('getErrorMessage', () => { - it('should extract message from Error objects', () => { - const error = new Error('Test message') - expect(getErrorMessage(error)).toBe('Test message') - }) - - it('should handle objects with message property', () => { - const errorObj = { message: 'Object message' } - expect(getErrorMessage(errorObj)).toBe('Object message') - }) - - it('should convert non-Error objects to string', () => { - const obj = { toString: () => 'Custom toString' } - expect(getErrorMessage(obj)).toBe('Custom toString') - }) - - it('should return empty string for undefined or null', () => { - expect(getErrorMessage(undefined)).toBe('') - expect(getErrorMessage(null)).toBe('') - }) - }) - describe('isAbortError', () => { it('should identify OpenAI abort errors by message', () => { const openaiError = { message: 'Request was aborted.' } diff --git a/src/renderer/src/utils/error.ts b/src/renderer/src/utils/error.ts index 5ab99f4bcd..54639a333a 100644 --- a/src/renderer/src/utils/error.ts +++ b/src/renderer/src/utils/error.ts @@ -1,4 +1,5 @@ import { loggerService } from '@logger' +import { AISDKError, APICallError } from 'ai' import { t } from 'i18next' const logger = loggerService.withContext('Utils:error') @@ -53,26 +54,6 @@ export function formatErrorMessage(error: any): string { } } -export function formatMessageError(error: any): Record { - try { - const detailedError = getErrorDetails(error) - delete detailedError?.headers - delete detailedError?.stack - delete detailedError?.request_id - return detailedError - } catch (e) { - try { - return { message: String(error) } - } catch { - return { message: 'Error: Unable to format error message' } - } - } -} - -export function getErrorMessage(error: any): string { - return error?.message || error?.toString() || '' -} - export const isAbortError = (error: any): boolean => { // Convert message to string for consistent checking const errorMessage = String(error?.message || '') @@ -106,3 +87,23 @@ export const formatMcpError = (error: any) => { } return error.message } + +export const serializeError = (error: AISDKError) => { + const baseError = { + name: error.name, + message: error.message, + stack: error.stack, + cause: error.cause ? String(error.cause) : undefined + } + if (APICallError.isInstance(error)) { + let content = error.message === '' ? error.responseBody || 'Unknown error' : error.message + try { + const obj = JSON.parse(content) + content = obj.error.message + } catch (e: any) { + logger.warn('Error parsing error response body:', e) + } + return { ...baseError, status: error.statusCode, url: error.url, message: content } + } + return baseError +}