Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Task } from "../task/Task"
import { codebaseSearchTool } from "../tools/codebaseSearchTool"
import { experiments, EXPERIMENT_IDS } from "../../shared/experiments"
import { applyDiffToolLegacy } from "../tools/applyDiffTool"
import { t } from "../../i18n"

/**
* Processes and presents assistant message content to the user interface.
Expand Down Expand Up @@ -316,7 +317,10 @@ export async function presentAssistantMessage(cline: Task) {

await cline.say(
"error",
`Error ${action}:\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,
t("tools:errors.toolExecutionError", {
action,
error: error.message ?? JSON.stringify(serializeError(error), null, 2),
}),
)

pushToolResult(formatResponse.toolError(errorString))
Expand Down
22 changes: 12 additions & 10 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,12 +1053,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
}

async sayAndCreateMissingParamError(toolName: ToolName, paramName: string, relPath?: string) {
await this.say(
"error",
`Roo tried to use ${toolName}${
relPath ? ` for '${relPath.toPosix()}'` : ""
} without value for required parameter '${paramName}'. Retrying...`,
)
const errorMessage = relPath
? t("tools:errors.missingRequiredParameter.withPath", {
toolName,
relPath: relPath.toPosix(),
paramName,
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactoring! The conditional logic for handling the path parameter makes the error messages more contextual. This is a good improvement over the original hardcoded message.

: t("tools:errors.missingRequiredParameter.withoutPath", {
toolName,
paramName,
})
await this.say("error", errorMessage)
return formatResponse.toolError(formatResponse.missingToolParameterError(paramName))
}

Expand Down Expand Up @@ -2135,10 +2140,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// If there's no assistant_responses, that means we got no text
// or tool_use content blocks from API which we should assume is
// an error.
await this.say(
"error",
"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.",
)
await this.say("error", t("common:errors.unexpectedApiResponse"))

await this.addToApiConversationHistory({
role: "assistant",
Expand Down
7 changes: 7 additions & 0 deletions src/core/tools/__tests__/useMcpToolTool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ vi.mock("../../prompts/responses", () => ({

vi.mock("../../../i18n", () => ({
t: vi.fn((key: string, params?: any) => {
// Handle the new tools error messages
if (key === "tools:errors.missingRequiredParameter.withPath" && params) {
return `Roo tried to use ${params.toolName} for '${params.relPath}' without value for required parameter '${params.paramName}'. Retrying...`
}
if (key === "tools:errors.missingRequiredParameter.withoutPath" && params) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job updating the test mocks to handle the new i18n keys. This ensures the tests continue to work correctly with the internationalized messages.

return `Roo tried to use ${params.toolName} without value for required parameter '${params.paramName}'. Retrying...`
}
if (key === "mcp:errors.invalidJsonArgument" && params?.toolName) {
return `Roo tried to use ${params.toolName} with an invalid JSON argument. Retrying...`
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/tools/applyDiffTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { fileExistsAtPath } from "../../utils/fs"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { unescapeHtmlEntities } from "../../utils/text-normalization"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { t } from "../../i18n"

export async function applyDiffToolLegacy(
cline: Task,
Expand Down Expand Up @@ -82,7 +83,7 @@ export async function applyDiffToolLegacy(
if (!fileExists) {
cline.consecutiveMistakeCount++
cline.recordToolError("apply_diff")
const formattedError = `File does not exist at path: ${absolutePath}\n\n<error_details>\nThe specified file could not be found. Please verify the file path and try again.\n</error_details>`
const formattedError = t("tools:errors.fileNotFound", { path: absolutePath })
await cline.say("error", formattedError)
pushToolResult(formattedError)
return
Expand Down
3 changes: 2 additions & 1 deletion src/core/tools/askFollowupQuestionTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Task } from "../task/Task"
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { formatResponse } from "../prompts/responses"
import { parseXml } from "../../utils/xml"
import { t } from "../../i18n"

export async function askFollowupQuestionTool(
cline: Task,
Expand Down Expand Up @@ -48,7 +49,7 @@ export async function askFollowupQuestionTool(
} catch (error) {
cline.consecutiveMistakeCount++
cline.recordToolError("ask_followup_question")
await cline.say("error", `Failed to parse operations: ${error.message}`)
await cline.say("error", t("tools:errors.parseOperationsFailed", { error: error.message }))
pushToolResult(formatResponse.toolError("Invalid operations xml format"))
return
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/tools/insertContentTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { fileExistsAtPath } from "../../utils/fs"
import { insertGroups } from "../diff/insert-groups"
import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { t } from "../../i18n"

export async function insertContentTool(
cline: Task,
Expand Down Expand Up @@ -86,7 +87,7 @@ export async function insertContentTool(
if (lineNumber > 1) {
cline.consecutiveMistakeCount++
cline.recordToolError("insert_content")
const formattedError = `Cannot insert content at line ${lineNumber} into a non-existent file. For new files, 'line' must be 0 (to append) or 1 (to insert at the beginning).`
const formattedError = t("tools:errors.insertContentNewFile", { lineNumber })
await cline.say("error", formattedError)
pushToolResult(formattedError)
return
Expand Down
10 changes: 6 additions & 4 deletions src/core/tools/searchAndReplaceTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { fileExistsAtPath } from "../../utils/fs"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { t } from "../../i18n"

/**
* Tool for performing search and replace operations on files
Expand Down Expand Up @@ -135,7 +136,7 @@ export async function searchAndReplaceTool(
cline.consecutiveMistakeCount++
cline.recordToolError("search_and_replace")
const formattedError = formatResponse.toolError(
`File does not exist at path: ${absolutePath}\nThe specified file could not be found. Please verify the file path and try again.`,
t("tools:errors.fileNotFoundSimple", { path: absolutePath }),
)
await cline.say("error", formattedError)
pushToolResult(formattedError)
Expand All @@ -152,9 +153,10 @@ export async function searchAndReplaceTool(
} catch (error) {
cline.consecutiveMistakeCount++
cline.recordToolError("search_and_replace")
const errorMessage = `Error reading file: ${absolutePath}\nFailed to read the file content: ${
error instanceof Error ? error.message : String(error)
}\nPlease verify file permissions and try again.`
const errorMessage = t("tools:errors.fileReadError", {
path: absolutePath,
error: error instanceof Error ? error.message : String(error),
})
const formattedError = formatResponse.toolError(errorMessage)
await cline.say("error", formattedError)
pushToolResult(formattedError)
Expand Down
8 changes: 5 additions & 3 deletions src/core/tools/writeToFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { detectCodeOmission } from "../../integrations/editor/detect-omission"
import { unescapeHtmlEntities } from "../../utils/text-normalization"
import { DEFAULT_WRITE_DELAY_MS } from "@roo-code/types"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { t } from "../../i18n"

export async function writeToFileTool(
cline: Task,
Expand Down Expand Up @@ -145,9 +146,10 @@ export async function writeToFileTool(
// Use more specific error message for line_count that provides guidance based on the situation
await cline.say(
"error",
`Roo tried to use write_to_file${
relPath ? ` for '${relPath.toPosix()}'` : ""
} but the required parameter 'line_count' was missing or truncated after ${actualLineCount} lines of content were written. Retrying...`,
t("tools:errors.lineCountMissing", {
relPath: relPath ? ` for '${relPath.toPosix()}'` : "",
actualLineCount,
}),
)

pushToolResult(
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@
},
"roo": {
"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud."
}
},
"unexpectedApiResponse": "Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output."
},
"warnings": {
"no_terminal_content": "No terminal content selected",
Expand Down
13 changes: 13 additions & 0 deletions src/i18n/locales/en/tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,18 @@
"errors": {
"policy_restriction": "Failed to create new task due to policy restrictions."
}
},
"errors": {
"missingRequiredParameter": {
"withPath": "Roo tried to use {{toolName}} for '{{relPath}}' without value for required parameter '{{paramName}}'. Retrying...",
"withoutPath": "Roo tried to use {{toolName}} without value for required parameter '{{paramName}}'. Retrying..."
},
"lineCountMissing": "Roo tried to use write_to_file{{relPath}} but the required parameter 'line_count' was missing or truncated after {{actualLineCount}} lines of content were written. Retrying...",
Copy link

Copilot AI Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space between 'write_to_file' and '{{relPath}}' placeholder. This will result in incorrect formatting when relPath is empty or when concatenating the tool name with the path.

Suggested change
"lineCountMissing": "Roo tried to use write_to_file{{relPath}} but the required parameter 'line_count' was missing or truncated after {{actualLineCount}} lines of content were written. Retrying...",
"lineCountMissing": "Roo tried to use write_to_file {{relPath}} but the required parameter 'line_count' was missing or truncated after {{actualLineCount}} lines of content were written. Retrying...",

Copilot uses AI. Check for mistakes.
"parseOperationsFailed": "Failed to parse operations: {{error}}",
"fileNotFound": "File does not exist at path: {{path}}\n\n<error_details>\nThe specified file could not be found. Please verify the file path and try again.\n</error_details>",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that some error messages include HTML-like tags (<error_details>) while others don't. For consistency across the codebase, should we standardize whether error details are wrapped in these tags or not?

For example, fileNotFound includes the tags while fileNotFoundSimple doesn't.

"fileNotFoundSimple": "File does not exist at path: {{path}}\nThe specified file could not be found. Please verify the file path and try again.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor suggestion: Several error messages follow similar patterns (e.g., "File does not exist at path: {{path}}"). In the future, these could potentially be further abstracted into a single parameterized message to reduce duplication. But this current approach is perfectly fine for now.

"fileReadError": "Error reading file: {{path}}\nFailed to read the file content: {{error}}\nPlease verify file permissions and try again.",
"insertContentNewFile": "Cannot insert content at line {{lineNumber}} into a non-existent file. For new files, 'line' must be 0 (to append) or 1 (to insert at the beginning).",
"toolExecutionError": "Error {{action}}:\n{{error}}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on adding these error message keys! The structure is clear and follows good i18n practices with parameter interpolation.

One observation: The PR description mentions that translations for the other 17 supported languages will be handled separately. Would it be helpful to create placeholder entries in the other locale files now, or track this as a follow-up task?

}
}
Loading