Skip to content

Commit 75a6bc1

Browse files
authored
Safe JSON parse in ChatRow (RooCodeInc#2666)
1 parent 7fab2f7 commit 75a6bc1

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, { memo, useEffect, useMemo, useRef, useState } from "react"
44
import { useSize } from "react-use"
55
import { useCopyToClipboard } from "../../utils/clipboard"
66
import { useTranslation, Trans } from "react-i18next"
7+
import { safeJsonParse } from "../../utils/json"
78
import {
89
ClineApiReqInfo,
910
ClineAskUseMcpServer,
@@ -92,8 +93,8 @@ export const ChatRowContent = ({
9293

9394
const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => {
9495
if (message.text !== null && message.text !== undefined && message.say === "api_req_started") {
95-
const info: ClineApiReqInfo = JSON.parse(message.text)
96-
return [info.cost, info.cancelReason, info.streamingFailedMessage]
96+
const info = safeJsonParse<ClineApiReqInfo>(message.text)
97+
return [info?.cost, info?.cancelReason, info?.streamingFailedMessage]
9798
}
9899

99100
return [undefined, undefined, undefined]
@@ -147,7 +148,10 @@ export const ChatRowContent = ({
147148
<span style={{ color: normalColor, fontWeight: "bold" }}>{t("chat:runCommand.title")}:</span>,
148149
]
149150
case "use_mcp_server":
150-
const mcpServerUse = JSON.parse(message.text || "{}") as ClineAskUseMcpServer
151+
const mcpServerUse = safeJsonParse<ClineAskUseMcpServer>(message.text)
152+
if (mcpServerUse === undefined) {
153+
return [null, null]
154+
}
151155
return [
152156
isMcpServerResponding ? (
153157
<ProgressIndicator />
@@ -250,14 +254,14 @@ export const ChatRowContent = ({
250254

251255
const tool = useMemo(() => {
252256
if (message.ask === "tool" || message.say === "tool") {
253-
return JSON.parse(message.text || "{}") as ClineSayTool
257+
return safeJsonParse<ClineSayTool>(message.text)
254258
}
255259
return null
256260
}, [message.ask, message.say, message.text])
257261

258262
const followUpData = useMemo(() => {
259263
if (message.type === "ask" && message.ask === "followup" && !message.partial) {
260-
return JSON.parse(message.text || "{}")
264+
return safeJsonParse<any>(message.text)
261265
}
262266
return null
263267
}, [message.type, message.ask, message.partial, message.text])
@@ -830,7 +834,7 @@ export const ChatRowContent = ({
830834
{isExpanded && (
831835
<div style={{ marginTop: "10px" }}>
832836
<CodeAccordian
833-
code={JSON.parse(message.text || "{}").request}
837+
code={safeJsonParse<any>(message.text)?.request}
834838
language="markdown"
835839
isExpanded={true}
836840
onToggleExpand={onToggleExpand}
@@ -897,15 +901,15 @@ export const ChatRowContent = ({
897901
</div>
898902
)
899903
case "user_feedback_diff":
900-
const tool = JSON.parse(message.text || "{}") as ClineSayTool
904+
const tool = safeJsonParse<ClineSayTool>(message.text)
901905
return (
902906
<div
903907
style={{
904908
marginTop: -10,
905909
width: "100%",
906910
}}>
907911
<CodeAccordian
908-
diff={tool.diff!}
912+
diff={tool?.diff!}
909913
isFeedback={true}
910914
isExpanded={isExpanded}
911915
onToggleExpand={onToggleExpand}
@@ -1113,7 +1117,10 @@ export const ChatRowContent = ({
11131117
</>
11141118
)
11151119
case "use_mcp_server":
1116-
const useMcpServer = JSON.parse(message.text || "{}") as ClineAskUseMcpServer
1120+
const useMcpServer = safeJsonParse<ClineAskUseMcpServer>(message.text)
1121+
if (!useMcpServer) {
1122+
return null
1123+
}
11171124
const server = mcpServers.find((server) => server.name === useMcpServer.serverName)
11181125
return (
11191126
<>

webview-ui/src/utils/json.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Safely parses JSON without crashing on invalid input
3+
* @param jsonString The string to parse
4+
* @param defaultValue Value to return if parsing fails
5+
* @returns Parsed JSON object or defaultValue if parsing fails
6+
*/
7+
export function safeJsonParse<T>(jsonString: string | null | undefined, defaultValue?: T): T | undefined {
8+
if (!jsonString) return defaultValue
9+
10+
try {
11+
return JSON.parse(jsonString) as T
12+
} catch (error) {
13+
// Log the error to the console for debugging
14+
console.error("Error parsing JSON:", error)
15+
return defaultValue
16+
}
17+
}

0 commit comments

Comments
 (0)