From 68abab9a90552f52609485e3d28e40cf51447e7d Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Thu, 14 Aug 2025 14:04:22 +0100 Subject: [PATCH] Update error handling --- .../tools/convert/useConvertOperation.ts | 11 +-- .../hooks/tools/shared/useToolOperation.ts | 6 +- frontend/src/utils/toolErrorHandler.ts | 79 +++++++++++++++---- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/frontend/src/hooks/tools/convert/useConvertOperation.ts b/frontend/src/hooks/tools/convert/useConvertOperation.ts index f14302aaa..91c76bd82 100644 --- a/frontend/src/hooks/tools/convert/useConvertOperation.ts +++ b/frontend/src/hooks/tools/convert/useConvertOperation.ts @@ -5,6 +5,7 @@ import { ConvertParameters } from './useConvertParameters'; import { detectFileExtension } from '../../../utils/fileUtils'; import { createFileFromApiResponse } from '../../../utils/fileResponseUtils'; import { useToolOperation, ToolOperationConfig } from '../shared/useToolOperation'; +import { createStandardErrorHandler } from '../../../utils/toolErrorHandler'; import { getEndpointUrl, isImageFormat, isWebFormat } from '../../../utils/convertUtils'; const shouldProcessFilesSeparately = ( @@ -134,14 +135,6 @@ export const useConvertOperation = () => { buildFormData, // Not used with customProcessor but required filePrefix: 'converted_', customProcessor: customConvertProcessor, // Convert handles its own routing - getErrorMessage: (error) => { - if (error.response?.data && typeof error.response.data === 'string') { - return error.response.data; - } - if (error.message) { - return error.message; - } - return t("convert.errorConversion", "An error occurred while converting the file."); - } + getErrorMessage: createStandardErrorHandler(t('convert.errorConversion', 'An error occurred while converting the file.')) }); }; diff --git a/frontend/src/hooks/tools/shared/useToolOperation.ts b/frontend/src/hooks/tools/shared/useToolOperation.ts index 9fa883490..c3b16b133 100644 --- a/frontend/src/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/hooks/tools/shared/useToolOperation.ts @@ -5,7 +5,7 @@ import { useFileContext } from '../../../contexts/FileContext'; import { useToolState, type ProcessingProgress } from './useToolState'; import { useToolApiCalls, type ApiCallsConfig } from './useToolApiCalls'; import { useToolResources } from './useToolResources'; -import { extractErrorMessage } from '../../../utils/toolErrorHandler'; +import { extractErrorMessage, type ToolError } from '../../../utils/toolErrorHandler'; import { createOperation } from '../../../utils/toolOperationTracker'; import { ResponseHandler } from '../../../utils/toolResponseProcessor'; @@ -60,7 +60,7 @@ export interface ToolOperationConfig { customProcessor?: (params: TParams, files: File[]) => Promise; /** Extract user-friendly error messages from API errors */ - getErrorMessage?: (error: any) => string; + getErrorMessage?: (error: unknown) => string; } /** @@ -204,7 +204,7 @@ export const useToolOperation = ( markOperationApplied(fileId, operationId); } - } catch (error: any) { + } catch (error: unknown) { const errorMessage = config.getErrorMessage?.(error) || extractErrorMessage(error); actions.setError(errorMessage); actions.setStatus(''); diff --git a/frontend/src/utils/toolErrorHandler.ts b/frontend/src/utils/toolErrorHandler.ts index ee1efe4d9..eb230cf55 100644 --- a/frontend/src/utils/toolErrorHandler.ts +++ b/frontend/src/utils/toolErrorHandler.ts @@ -2,16 +2,72 @@ * Standardized error handling utilities for tool operations */ +/** + * Standard error type that covers common error patterns + */ +export interface ToolError { + message?: string; + response?: { + data?: string | unknown; + status?: number; + }; +} + +/** + * Extract error message from JSON response data + */ +const extractFromJsonData = (data: unknown): string | null => { + if (typeof data === 'object' && data !== null) { + const obj = data as Record; + // Common JSON error patterns + if (typeof obj.message === 'string' && obj.message.trim()) { + return obj.message.trim(); + } + if (typeof obj.error === 'string' && obj.error.trim()) { + return obj.error.trim(); + } + if (typeof obj.detail === 'string' && obj.detail.trim()) { + return obj.detail.trim(); + } + } + return null; +}; + /** * Default error extractor that follows the standard pattern */ -export const extractErrorMessage = (error: any): string => { - if (error.response?.data && typeof error.response.data === 'string') { - return error.response.data; +export const extractErrorMessage = (error: unknown): string => { + const typedError = error as ToolError; + + // Try response.data first + if (typedError.response?.data) { + // Handle string response.data + if (typeof typedError.response.data === 'string' && typedError.response.data.trim()) { + return typedError.response.data.trim(); + } + + // Handle JSON response.data + const jsonMessage = extractFromJsonData(typedError.response.data); + if (jsonMessage) { + return jsonMessage; + } + + // Handle Blob or other non-string data gracefully + if (typedError.response.data instanceof Blob) { + return 'Server returned an error response'; + } } - if (error.message) { - return error.message; + + // Fallback to error.message + if (typedError.message && typedError.message.trim()) { + return typedError.message.trim(); } + + // Add HTTP status context if available + if (typedError.response?.status) { + return `Server error (${typedError.response.status})`; + } + return 'Operation failed'; }; @@ -21,13 +77,8 @@ export const extractErrorMessage = (error: any): string => { * @returns Error handler function that follows the standard pattern */ export const createStandardErrorHandler = (fallbackMessage: string) => { - return (error: any): string => { - if (error.response?.data && typeof error.response.data === 'string') { - return error.response.data; - } - if (error.message) { - return error.message; - } - return fallbackMessage; + return (error: unknown): string => { + return extractErrorMessage(error) || fallbackMessage; }; -}; \ No newline at end of file +}; +