Skip to content

Commit 011126a

Browse files
committed
refactor(read_file): emit structured FILE_NOT_FOUND error and render friendly UI; avoid duplicates with Set; suppress per-file XML to prevent duplicate signals
1 parent 4028d43 commit 011126a

File tree

2 files changed

+65
-23
lines changed

2 files changed

+65
-23
lines changed

src/core/tools/readFileTool.ts

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ export async function readFileTool(
198198
lineRanges: entry.lineRanges,
199199
}))
200200

201-
// Track files that were not found
202-
const notFoundFiles: string[] = []
201+
// Track files that were not found (use Set to avoid duplicates)
202+
const notFoundFiles = new Set<string>()
203203

204204
// Function to update file result status
205205
const updateFileResult = (path: string, updates: Partial<FileResult>) => {
@@ -625,12 +625,13 @@ export async function readFileTool(
625625

626626
if (isFileNotFound) {
627627
// Track the missing file
628-
notFoundFiles.push(relPath)
628+
notFoundFiles.add(relPath)
629629

630630
updateFileResult(relPath, {
631631
status: "error",
632632
error: "File not found",
633-
xmlContent: `<file><path>${relPath}</path><error>File not found: The requested file does not exist in the current workspace.</error></file>`,
633+
// Suppress per-file XML for not-found to avoid duplicate UI signals
634+
xmlContent: undefined,
634635
})
635636
// Don't call handleError for file not found - we'll emit a unified message later
636637
} else {
@@ -646,14 +647,11 @@ export async function readFileTool(
646647
}
647648

648649
// Emit a unified file not found error message if any files were not found
649-
if (notFoundFiles.length > 0) {
650-
await cline.say(
651-
"error",
652-
JSON.stringify({
653-
filePaths: notFoundFiles,
654-
error: "The requested files do not exist in the current workspace.",
655-
}),
656-
)
650+
{
651+
const missing = Array.from(notFoundFiles)
652+
if (missing.length > 0) {
653+
await cline.say("error", JSON.stringify({ code: "FILE_NOT_FOUND", filePaths: missing }))
654+
}
657655
}
658656

659657
// Generate final XML result from all file results
@@ -742,14 +740,15 @@ export async function readFileTool(
742740

743741
if (isFileNotFound) {
744742
// Track the missing file
745-
notFoundFiles.push(relPath)
743+
notFoundFiles.add(relPath)
746744

747745
// If we have file results, update the first one with the error
748746
if (fileResults.length > 0) {
749747
updateFileResult(relPath, {
750748
status: "error",
751749
error: "File not found",
752-
xmlContent: `<file><path>${relPath}</path><error>File not found: The requested file does not exist in the current workspace.</error></file>`,
750+
// Suppress per-file XML for not-found to avoid duplicate UI signals
751+
xmlContent: undefined,
753752
})
754753
}
755754
// Don't call handleError for file not found - we'll emit a unified message later
@@ -767,14 +766,11 @@ export async function readFileTool(
767766
}
768767

769768
// Emit a unified file not found error message if any files were not found
770-
if (notFoundFiles.length > 0) {
771-
await cline.say(
772-
"error",
773-
JSON.stringify({
774-
filePaths: notFoundFiles,
775-
error: "The requested files do not exist in the current workspace.",
776-
}),
777-
)
769+
{
770+
const missing = Array.from(notFoundFiles)
771+
if (missing.length > 0) {
772+
await cline.say("error", JSON.stringify({ code: "FILE_NOT_FOUND", filePaths: missing }))
773+
}
778774
}
779775

780776
// Generate final XML result from all file results

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1130,7 +1130,52 @@ export const ChatRowContent = ({
11301130
/>
11311131
</div>
11321132
)
1133-
case "error":
1133+
case "error": {
1134+
const parsed = safeJsonParse<any>(message.text)
1135+
if (parsed?.code === "FILE_NOT_FOUND") {
1136+
const filePaths: string[] = Array.isArray(parsed.filePaths) ? parsed.filePaths : []
1137+
const isPlural = filePaths.length > 1
1138+
const header = isPlural
1139+
? t("chat:fileOperations.filesNotFound")
1140+
: t("chat:fileOperations.fileNotFound")
1141+
const description = isPlural
1142+
? t("chat:fileOperations.filesNotFoundMessage")
1143+
: t("chat:fileOperations.fileNotFoundMessage")
1144+
1145+
return (
1146+
<div>
1147+
<div style={headerStyle}>
1148+
<span
1149+
className="codicon codicon-warning"
1150+
style={{
1151+
color: "var(--vscode-editorWarning-foreground)",
1152+
marginBottom: "-1.5px",
1153+
}}
1154+
/>
1155+
<span style={{ fontWeight: "bold" }}>{header}</span>
1156+
</div>
1157+
<div
1158+
style={{
1159+
padding: "8px",
1160+
backgroundColor: "var(--vscode-editor-background)",
1161+
border: "1px solid var(--vscode-editorGroup-border)",
1162+
borderRadius: 4,
1163+
}}>
1164+
<p style={{ ...pStyle, color: "var(--vscode-editor-foreground)" }}>{description}</p>
1165+
{filePaths.length > 0 && (
1166+
<ul style={{ marginTop: 8, paddingLeft: 18 }}>
1167+
{filePaths.map((p, i) => (
1168+
<li key={i}>
1169+
<code>{p}</code>
1170+
</li>
1171+
))}
1172+
</ul>
1173+
)}
1174+
</div>
1175+
</div>
1176+
)
1177+
}
1178+
11341179
return (
11351180
<>
11361181
{title && (
@@ -1142,6 +1187,7 @@ export const ChatRowContent = ({
11421187
<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>{message.text}</p>
11431188
</>
11441189
)
1190+
}
11451191
case "completion_result":
11461192
return (
11471193
<>

0 commit comments

Comments
 (0)