From abaf86a6ec029a9bd7a9d84434fd44bfe6c4bab7 Mon Sep 17 00:00:00 2001 From: thibaud-leclere Date: Tue, 14 Apr 2026 11:19:42 +0200 Subject: [PATCH] fix: display tauri error messages instead of object dumps --- src/components/projects/ProjectForm.tsx | 4 +-- src/components/settings/SettingsPage.tsx | 7 ++-- src/components/tickets/TicketDetail.tsx | 11 ++++--- src/components/trackers/TrackerConfig.tsx | 5 +-- src/lib/errors.ts | 40 +++++++++++++++++++++++ 5 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 src/lib/errors.ts diff --git a/src/components/projects/ProjectForm.tsx b/src/components/projects/ProjectForm.tsx index 6c46845..64730c4 100644 --- a/src/components/projects/ProjectForm.tsx +++ b/src/components/projects/ProjectForm.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { open } from "@tauri-apps/plugin-dialog"; import { createProject, getProject, updateProject } from "../../lib/api"; +import { getErrorMessage } from "../../lib/errors"; export default function ProjectForm() { const navigate = useNavigate(); @@ -49,8 +50,7 @@ export default function ProjectForm() { window.dispatchEvent(new Event("orchai:refresh-projects")); navigate("/"); } catch (err: unknown) { - const message = err instanceof Error ? err.message : String(err); - setError(message); + setError(getErrorMessage(err)); } finally { setLoading(false); } diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index 170a092..5717a98 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -5,6 +5,7 @@ import { deleteTuleapCredentials, testTuleapConnection, } from "../../lib/api"; +import { getErrorMessage } from "../../lib/errors"; import type { TuleapCredentialsSafe } from "../../lib/types"; export default function SettingsPage() { @@ -43,7 +44,7 @@ export default function SettingsPage() { setPassword(""); setSuccess("Credentials saved."); } catch (err: unknown) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } finally { setSaving(false); } @@ -56,7 +57,7 @@ export default function SettingsPage() { const msg = await testTuleapConnection(); setSuccess(msg); } catch (err: unknown) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } finally { setTesting(false); } @@ -74,7 +75,7 @@ export default function SettingsPage() { setPassword(""); setSuccess("Credentials deleted."); } catch (err: unknown) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } finally { setDeleting(false); } diff --git a/src/components/tickets/TicketDetail.tsx b/src/components/tickets/TicketDetail.tsx index 1fd5ac0..3a9ef9f 100644 --- a/src/components/tickets/TicketDetail.tsx +++ b/src/components/tickets/TicketDetail.tsx @@ -10,6 +10,7 @@ import { getWorktreeDiff, retryTicket, } from "../../lib/api"; +import { getErrorMessage } from "../../lib/errors"; import type { ProcessedTicket, Worktree } from "../../lib/types"; function statusBadgeClass(status: string): string { @@ -88,7 +89,7 @@ export default function TicketDetail() { setDiff(null); } } catch (err) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } } @@ -103,7 +104,7 @@ export default function TicketDetail() { await retryTicket(ticketId); await loadData(); } catch (err) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } setLoading(false); } @@ -115,7 +116,7 @@ export default function TicketDetail() { await cancelTicket(ticketId); await loadData(); } catch (err) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } setLoading(false); } @@ -128,7 +129,7 @@ export default function TicketDetail() { await applyFixToBranch(worktree.id, targetBranch); await loadData(); } catch (err) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } setLoading(false); } @@ -142,7 +143,7 @@ export default function TicketDetail() { setWorktree(null); setDiff(null); } catch (err) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } setLoading(false); } diff --git a/src/components/trackers/TrackerConfig.tsx b/src/components/trackers/TrackerConfig.tsx index 8b05c15..022b843 100644 --- a/src/components/trackers/TrackerConfig.tsx +++ b/src/components/trackers/TrackerConfig.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { addTracker, getTrackerFields } from "../../lib/api"; +import { getErrorMessage } from "../../lib/errors"; import type { FilterGroup, TrackerField, AgentConfig } from "../../lib/types"; import FilterBuilder from "./FilterBuilder"; @@ -29,7 +30,7 @@ export default function TrackerConfig() { setFields(result); setFieldsLoaded(true); } catch (err: unknown) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } finally { setFieldsLoading(false); } @@ -52,7 +53,7 @@ export default function TrackerConfig() { await addTracker(projectId, Number(trackerId), trackerLabel, pollingInterval, agentConfig, filters); navigate(`/projects/${projectId}`); } catch (err: unknown) { - setError(err instanceof Error ? err.message : String(err)); + setError(getErrorMessage(err)); } finally { setLoading(false); } diff --git a/src/lib/errors.ts b/src/lib/errors.ts new file mode 100644 index 0000000..b34705e --- /dev/null +++ b/src/lib/errors.ts @@ -0,0 +1,40 @@ +export function getErrorMessage(err: unknown): string { + if (err instanceof Error) { + return err.message; + } + + if (typeof err === "string") { + return err; + } + + if (err && typeof err === "object") { + const withMessage = err as { message?: unknown; error?: unknown }; + + if (typeof withMessage.message === "string") { + return withMessage.message; + } + + if (typeof withMessage.error === "string") { + return withMessage.error; + } + + if ( + withMessage.message && + typeof withMessage.message === "object" && + "message" in withMessage.message + ) { + const nested = withMessage.message as { message?: unknown }; + if (typeof nested.message === "string") { + return nested.message; + } + } + + try { + return JSON.stringify(err); + } catch { + // fallthrough to String below + } + } + + return String(err); +}