diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 05005e46a1..c468dfccfc 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -4,6 +4,7 @@ import React, { memo, useEffect, useMemo, useRef, useState } from "react" import { useSize } from "react-use" import { useCopyToClipboard } from "../../utils/clipboard" import { useTranslation, Trans } from "react-i18next" +import { safeJsonParse } from "../../utils/json" import { ClineApiReqInfo, ClineAskUseMcpServer, @@ -92,8 +93,8 @@ export const ChatRowContent = ({ const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => { if (message.text !== null && message.text !== undefined && message.say === "api_req_started") { - const info: ClineApiReqInfo = JSON.parse(message.text) - return [info.cost, info.cancelReason, info.streamingFailedMessage] + const info = safeJsonParse(message.text) + return [info?.cost, info?.cancelReason, info?.streamingFailedMessage] } return [undefined, undefined, undefined] @@ -147,7 +148,10 @@ export const ChatRowContent = ({ {t("chat:runCommand.title")}:, ] case "use_mcp_server": - const mcpServerUse = JSON.parse(message.text || "{}") as ClineAskUseMcpServer + const mcpServerUse = safeJsonParse(message.text) + if (mcpServerUse === undefined) { + return [null, null] + } return [ isMcpServerResponding ? ( @@ -250,14 +254,14 @@ export const ChatRowContent = ({ const tool = useMemo(() => { if (message.ask === "tool" || message.say === "tool") { - return JSON.parse(message.text || "{}") as ClineSayTool + return safeJsonParse(message.text) } return null }, [message.ask, message.say, message.text]) const followUpData = useMemo(() => { if (message.type === "ask" && message.ask === "followup" && !message.partial) { - return JSON.parse(message.text || "{}") + return safeJsonParse(message.text) } return null }, [message.type, message.ask, message.partial, message.text]) @@ -830,7 +834,7 @@ export const ChatRowContent = ({ {isExpanded && (
(message.text)?.request} language="markdown" isExpanded={true} onToggleExpand={onToggleExpand} @@ -897,7 +901,7 @@ export const ChatRowContent = ({
) case "user_feedback_diff": - const tool = JSON.parse(message.text || "{}") as ClineSayTool + const tool = safeJsonParse(message.text) return (
) case "use_mcp_server": - const useMcpServer = JSON.parse(message.text || "{}") as ClineAskUseMcpServer + const useMcpServer = safeJsonParse(message.text) + if (!useMcpServer) { + return null + } const server = mcpServers.find((server) => server.name === useMcpServer.serverName) return ( <> diff --git a/webview-ui/src/utils/json.ts b/webview-ui/src/utils/json.ts new file mode 100644 index 0000000000..5b5f396fb7 --- /dev/null +++ b/webview-ui/src/utils/json.ts @@ -0,0 +1,17 @@ +/** + * Safely parses JSON without crashing on invalid input + * @param jsonString The string to parse + * @param defaultValue Value to return if parsing fails + * @returns Parsed JSON object or defaultValue if parsing fails + */ +export function safeJsonParse(jsonString: string | null | undefined, defaultValue?: T): T | undefined { + if (!jsonString) return defaultValue + + try { + return JSON.parse(jsonString) as T + } catch (error) { + // Log the error to the console for debugging + console.error("Error parsing JSON:", error) + return defaultValue + } +}