Skip to content

Commit 287c8eb

Browse files
committed
feat: implement graceful visual and mechanism for file not found errors
- Modified readFileTool.ts to detect ENOENT errors and format them user-friendly - Created new FileNotFoundError component with proper styling and icons - Updated ChatRow.tsx to detect and display file not found errors using the new component - Added translations for the new error messages in English locale This provides a cleaner user experience when the LLM tries to read non-existent files, replacing technical error messages with clear, visually distinct notifications.
1 parent b975ced commit 287c8eb

File tree

4 files changed

+117
-7
lines changed

4 files changed

+117
-7
lines changed

src/core/tools/readFileTool.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,18 @@ export async function readFileTool(
610610
xmlContent: `<file><path>${relPath}</path>\n${xmlInfo}</file>`,
611611
})
612612
} catch (error) {
613-
const errorMsg = error instanceof Error ? error.message : String(error)
613+
let errorMsg = error instanceof Error ? error.message : String(error)
614+
let userFriendlyError = errorMsg
615+
616+
// Check if this is a file not found error
617+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
618+
userFriendlyError = `File not found: The requested file does not exist in the current workspace.`
619+
}
620+
614621
updateFileResult(relPath, {
615622
status: "error",
616-
error: `Error reading file: ${errorMsg}`,
617-
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
623+
error: userFriendlyError,
624+
xmlContent: `<file><path>${relPath}</path><error>${userFriendlyError}</error></file>`,
618625
})
619626
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
620627
}
@@ -694,14 +701,20 @@ export async function readFileTool(
694701
} catch (error) {
695702
// Handle all errors using per-file format for consistency
696703
const relPath = fileEntries[0]?.path || "unknown"
697-
const errorMsg = error instanceof Error ? error.message : String(error)
704+
let errorMsg = error instanceof Error ? error.message : String(error)
705+
let userFriendlyError = errorMsg
706+
707+
// Check if this is a file not found error
708+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
709+
userFriendlyError = `File not found: The requested file does not exist in the current workspace.`
710+
}
698711

699712
// If we have file results, update the first one with the error
700713
if (fileResults.length > 0) {
701714
updateFileResult(relPath, {
702715
status: "error",
703-
error: `Error reading file: ${errorMsg}`,
704-
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
716+
error: userFriendlyError,
717+
xmlContent: `<file><path>${relPath}</path><error>${userFriendlyError}</error></file>`,
705718
})
706719
}
707720

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import { CommandExecutionError } from "./CommandExecutionError"
4646
import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning"
4747
import { CondenseContextErrorRow, CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow"
4848
import CodebaseSearchResultsDisplay from "./CodebaseSearchResultsDisplay"
49+
import { FileNotFoundError } from "./FileNotFoundError"
4950

5051
interface ChatRowProps {
5152
message: ClineMessage
@@ -1053,6 +1054,18 @@ export const ChatRowContent = ({
10531054
case "api_req_finished":
10541055
return null // we should never see this message type
10551056
case "text":
1057+
// Check if this is a file not found error from read_file tool
1058+
if (message.text && message.text.includes("<files>") && message.text.includes("<error>")) {
1059+
// Parse the XML to extract file path and error message
1060+
const fileMatch = message.text.match(
1061+
/<file><path>([^<]+)<\/path><error>([^<]+)<\/error><\/file>/,
1062+
)
1063+
if (fileMatch && fileMatch[2].includes("File not found:")) {
1064+
const filePath = fileMatch[1]
1065+
return <FileNotFoundError filePath={filePath} />
1066+
}
1067+
}
1068+
10561069
return (
10571070
<div>
10581071
<Markdown markdown={message.text} partial={message.partial} />
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from "react"
2+
import { useTranslation } from "react-i18next"
3+
4+
interface FileNotFoundErrorProps {
5+
filePath: string
6+
isExpanded?: boolean
7+
onToggleExpand?: () => void
8+
}
9+
10+
export const FileNotFoundError: React.FC<FileNotFoundErrorProps> = ({
11+
filePath,
12+
isExpanded = false,
13+
onToggleExpand,
14+
}) => {
15+
const { t } = useTranslation()
16+
17+
const headerStyle: React.CSSProperties = {
18+
display: "flex",
19+
alignItems: "center",
20+
gap: "10px",
21+
marginBottom: "10px",
22+
wordBreak: "break-word",
23+
cursor: onToggleExpand ? "pointer" : "default",
24+
userSelect: "none",
25+
}
26+
27+
const containerStyle: React.CSSProperties = {
28+
backgroundColor: "var(--vscode-inputValidation-warningBackground)",
29+
border: "1px solid var(--vscode-inputValidation-warningBorder)",
30+
borderRadius: "4px",
31+
padding: "12px",
32+
marginTop: "8px",
33+
marginBottom: "8px",
34+
}
35+
36+
const iconStyle: React.CSSProperties = {
37+
color: "var(--vscode-editorWarning-foreground)",
38+
fontSize: "16px",
39+
marginBottom: "-1.5px",
40+
}
41+
42+
const titleStyle: React.CSSProperties = {
43+
color: "var(--vscode-editorWarning-foreground)",
44+
fontWeight: "bold",
45+
}
46+
47+
const pathStyle: React.CSSProperties = {
48+
fontFamily: "var(--vscode-editor-font-family)",
49+
fontSize: "var(--vscode-editor-font-size)",
50+
marginTop: "8px",
51+
marginBottom: "4px",
52+
wordBreak: "break-all",
53+
}
54+
55+
const messageStyle: React.CSSProperties = {
56+
color: "var(--vscode-foreground)",
57+
opacity: 0.9,
58+
fontSize: "var(--vscode-font-size)",
59+
}
60+
61+
return (
62+
<div style={containerStyle}>
63+
<div style={headerStyle} onClick={onToggleExpand}>
64+
<span className="codicon codicon-warning" style={iconStyle} />
65+
<span style={titleStyle}>{t("chat:fileOperations.fileNotFound")}</span>
66+
{onToggleExpand && (
67+
<div style={{ flexGrow: 1, display: "flex", justifyContent: "flex-end" }}>
68+
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`} />
69+
</div>
70+
)}
71+
</div>
72+
{(isExpanded || !onToggleExpand) && (
73+
<>
74+
<div style={pathStyle}>
75+
<code>{filePath}</code>
76+
</div>
77+
<div style={messageStyle}>{t("chat:fileOperations.fileNotFoundMessage")}</div>
78+
</>
79+
)}
80+
</div>
81+
)
82+
}

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@
187187
"didSearchReplace": "Roo performed search and replace on this file:",
188188
"wantsToInsert": "Roo wants to insert content into this file:",
189189
"wantsToInsertWithLineNumber": "Roo wants to insert content into this file at line {{lineNumber}}:",
190-
"wantsToInsertAtEnd": "Roo wants to append content to the end of this file:"
190+
"wantsToInsertAtEnd": "Roo wants to append content to the end of this file:",
191+
"fileNotFound": "File Not Found",
192+
"fileNotFoundMessage": "The requested file does not exist in the current workspace."
191193
},
192194
"directoryOperations": {
193195
"wantsToViewTopLevel": "Roo wants to view the top level files in this directory:",

0 commit comments

Comments
 (0)