mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 06:12:10 +03:00
fix(cli-tools): prevent React crash from object error in Apply handler
Two root causes fixed:
1. Schema: cliModelConfigSchema.apiKey was z.string().optional() but
the frontend sends null when cloudEnabled is true. Zod rejects null
for optional strings → validation 400 with structured error object.
Fix: z.string().nullable().optional()
2. Frontend: All 25 tool card error handlers used data.error directly
as React children. When data.error is an object ({message, details}),
React crashes with Error #31 (Objects are not valid as React child).
Fix: extract data.error.message when data.error is an object.
This commit is contained in:
@@ -116,7 +116,11 @@ export default function AntigravityToolCard({
|
||||
setSudoPassword("");
|
||||
fetchStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedStart") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) || t("failedStart"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -142,7 +146,11 @@ export default function AntigravityToolCard({
|
||||
setSudoPassword("");
|
||||
fetchStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedStop") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) || t("failedStop"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -197,7 +205,10 @@ export default function AntigravityToolCard({
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json();
|
||||
throw new Error(data.error || t("failedSaveMappings"));
|
||||
throw new Error(
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedSaveMappings")
|
||||
);
|
||||
}
|
||||
|
||||
setMessage({ type: "success", text: t("mappingsSaved") });
|
||||
|
||||
@@ -169,7 +169,12 @@ export default function ClaudeToolCard({
|
||||
settings: { ...prev?.settings, env },
|
||||
}));
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedApplySettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedApplySettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -191,7 +196,12 @@ export default function ClaudeToolCard({
|
||||
);
|
||||
setSelectedApiKey("");
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedResetSettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedResetSettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -257,7 +267,12 @@ export default function ClaudeToolCard({
|
||||
checkClaudeStatus();
|
||||
fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedRestore") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestore"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -118,7 +118,12 @@ export default function ClineToolCard({
|
||||
await fetchBackups();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
setMessage({ type: "error", text: data.error || t("failedRestoreBackup") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestoreBackup"),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setMessage({ type: "error", text: e.message });
|
||||
@@ -173,7 +178,10 @@ export default function ClineToolCard({
|
||||
await checkClineStatus();
|
||||
await fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failed") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text: (typeof data.error === "string" ? data.error : data.error?.message) || t("failed"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -195,7 +203,10 @@ export default function ClineToolCard({
|
||||
await checkClineStatus();
|
||||
await fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failed") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text: (typeof data.error === "string" ? data.error : data.error?.message) || t("failed"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -75,7 +75,12 @@ export default function CliproxyapiToolCard({ isExpanded, onToggle }) {
|
||||
await fetchStatus();
|
||||
if (action === "install" || action === "restart") await fetchUpdateInfo();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || `${action} failed` });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
`${action} failed`,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setMessage({ type: "error", text: err instanceof Error ? err.message : "Request failed" });
|
||||
|
||||
@@ -178,7 +178,12 @@ export default function CodexToolCard({
|
||||
setMessage({ type: "success", text: t("settingsApplied") });
|
||||
checkCodexStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedApplySettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedApplySettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -198,7 +203,12 @@ export default function CodexToolCard({
|
||||
setSelectedModel("");
|
||||
checkCodexStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedResetSettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedResetSettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -246,7 +256,12 @@ export default function CodexToolCard({
|
||||
setNewProfileName("");
|
||||
fetchProfiles();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedSaveProfile") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedSaveProfile"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -270,7 +285,12 @@ export default function CodexToolCard({
|
||||
checkCodexStatus();
|
||||
fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedActivateProfile") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedActivateProfile"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -318,7 +338,12 @@ export default function CodexToolCard({
|
||||
checkCodexStatus();
|
||||
fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedRestore") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestore"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -249,7 +249,12 @@ export default function DefaultToolCard({
|
||||
if (res.ok) {
|
||||
setMessage({ type: "success", text: data.message || t("configurationSaved") });
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedToSave") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedToSave"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -150,7 +150,12 @@ export default function DroidToolCard({
|
||||
setMessage({ type: "success", text: t("settingsApplied") });
|
||||
checkDroidStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedApplySettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedApplySettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -171,7 +176,12 @@ export default function DroidToolCard({
|
||||
setSelectedApiKeyId("");
|
||||
checkDroidStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedResetSettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedResetSettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -211,7 +221,12 @@ export default function DroidToolCard({
|
||||
checkDroidStatus();
|
||||
fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedRestore") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestore"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -104,7 +104,12 @@ export default function KiloToolCard({
|
||||
await fetchBackups();
|
||||
} else {
|
||||
const data = await res.json();
|
||||
setMessage({ type: "error", text: data.error || t("failedRestoreBackup") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestoreBackup"),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setMessage({ type: "error", text: e.message });
|
||||
@@ -159,7 +164,10 @@ export default function KiloToolCard({
|
||||
await checkKiloStatus();
|
||||
await fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failed") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text: (typeof data.error === "string" ? data.error : data.error?.message) || t("failed"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -181,7 +189,10 @@ export default function KiloToolCard({
|
||||
await checkKiloStatus();
|
||||
await fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failed") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text: (typeof data.error === "string" ? data.error : data.error?.message) || t("failed"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -152,7 +152,12 @@ export default function OpenClawToolCard({
|
||||
setMessage({ type: "success", text: t("settingsApplied") });
|
||||
checkOpenclawStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedApplySettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedApplySettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -173,7 +178,12 @@ export default function OpenClawToolCard({
|
||||
setSelectedApiKeyId("");
|
||||
checkOpenclawStatus();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedResetSettings") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedResetSettings"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
@@ -213,7 +223,12 @@ export default function OpenClawToolCard({
|
||||
checkOpenclawStatus();
|
||||
fetchBackups();
|
||||
} else {
|
||||
setMessage({ type: "error", text: data.error || t("failedRestore") });
|
||||
setMessage({
|
||||
type: "error",
|
||||
text:
|
||||
(typeof data.error === "string" ? data.error : data.error?.message) ||
|
||||
t("failedRestore"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
setMessage({ type: "error", text: error.message });
|
||||
|
||||
@@ -1708,7 +1708,7 @@ export const cliSettingsEnvSchema = z.object({
|
||||
|
||||
export const cliModelConfigSchema = z.object({
|
||||
baseUrl: z.string().trim().min(1, "baseUrl and model are required"),
|
||||
apiKey: z.string().optional(),
|
||||
apiKey: z.string().nullable().optional(),
|
||||
model: z.string().trim().min(1, "baseUrl and model are required"),
|
||||
reasoningEffort: z.enum(["none", "low", "medium", "high", "xhigh"]).optional(),
|
||||
wireApi: z.enum(["chat", "responses"]).optional(),
|
||||
|
||||
Reference in New Issue
Block a user