/** * Centralized API error message extraction * * The API client interceptor rejects with a plain object: { status, code, message, error } * This utility extracts the user-facing message from any error shape. */ interface ApiErrorLike { status?: number code?: number | string message?: string error?: string reason?: string metadata?: Record response?: { data?: { detail?: string message?: string code?: number | string } } } /** * Extract the error code from an API error object. */ export function extractApiErrorCode(err: unknown): string | undefined { if (!err || typeof err !== 'object') return undefined const e = err as ApiErrorLike const code = e.code ?? e.reason ?? e.response?.data?.code return code != null ? String(code) : undefined } /** * Extract a displayable error message from an API error. * * @param err - The caught error (unknown type) * @param fallback - Fallback message if none can be extracted (use t('common.error') or similar) * @param i18nMap - Optional map of error codes to i18n translated strings */ export function extractApiErrorMessage( err: unknown, fallback = 'Unknown error', i18nMap?: Record, ): string { if (!err) return fallback // Try i18n mapping by error code first if (i18nMap) { const code = extractApiErrorCode(err) if (code && i18nMap[code]) return i18nMap[code] } // Plain object from API client interceptor (most common case) if (typeof err === 'object' && err !== null) { const e = err as ApiErrorLike // Interceptor shape: { message, error } if (e.message) return e.message if (e.error) return e.error // Legacy axios shape: { response.data.detail } if (e.response?.data?.detail) return e.response.data.detail if (e.response?.data?.message) return e.response.data.message } // Standard Error if (err instanceof Error) return err.message // Last resort const str = String(err) return str === '[object Object]' ? fallback : str }