Просмотр исходного кода

ux: improved error messages and documentation links (#9777)

* Minor ui tweaks

* Basic setup for richer API request errors

* Better errors messages and contact link

* i18n

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

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>

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

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>

* Empty better than null

* Update webview-ui/src/i18n/locales/nl/chat.json

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* i18n

* Start retryAttempt at 1

* Reverse retryAttempt number, just ommit it from the message

---------

Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Bruno Bergher 4 недель назад
Родитель
Сommit
0eddc97d13

+ 14 - 22
src/core/task/Task.ts

@@ -3775,17 +3775,19 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 			if (finalDelay <= 0) return
 
 			// Build header text; fall back to error message if none provided
-			let headerText = header
-			if (!headerText) {
-				if (error?.error?.metadata?.raw) {
-					headerText = JSON.stringify(error.error.metadata.raw, null, 2)
-				} else if (error?.message) {
-					headerText = error.message
-				} else {
-					headerText = "Unknown error"
-				}
+			let headerText
+			if (error.status) {
+				// This sets the message as just the error code, for which
+				// ChatRow knows how to handle and use an i18n'd error string
+				// In development, hardcode headerText to an HTTP status code to check it
+				headerText = error.status
+			} else if (error?.message) {
+				headerText = error.message
+			} else {
+				headerText = "Unknown error"
 			}
-			headerText = headerText ? `${headerText}\n\n` : ""
+
+			headerText = headerText ? `${headerText}\n` : ""
 
 			// Show countdown timer with exponential backoff
 			for (let i = finalDelay; i > 0; i--) {
@@ -3794,21 +3796,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 					throw new Error(`[Task#${this.taskId}] Aborted during retry countdown`)
 				}
 
-				await this.say(
-					"api_req_retry_delayed",
-					`${headerText}Retry attempt ${retryAttempt + 1}\nRetrying in ${i} seconds...`,
-					undefined,
-					true,
-				)
+				await this.say("api_req_retry_delayed", `${headerText}\n↻ ${i}s...`, undefined, true)
 				await delay(1000)
 			}
 
-			await this.say(
-				"api_req_retry_delayed",
-				`${headerText}Retry attempt ${retryAttempt + 1}\nRetrying now...`,
-				undefined,
-				false,
-			)
+			await this.say("api_req_retry_delayed", headerText, undefined, false)
 		} catch (err) {
 			console.error("Exponential backoff failed:", err)
 		}

+ 50 - 17
webview-ui/src/components/chat/ChatRow.tsx

@@ -38,7 +38,7 @@ import { Markdown } from "./Markdown"
 import { CommandExecution } from "./CommandExecution"
 import { CommandExecutionError } from "./CommandExecutionError"
 import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning"
-import { CondenseContextErrorRow, CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow"
+import { CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow"
 import CodebaseSearchResultsDisplay from "./CodebaseSearchResultsDisplay"
 import { appendImages } from "@src/utils/imageUtils"
 import { McpExecution } from "./McpExecution"
@@ -162,7 +162,7 @@ export const ChatRowContent = ({
 	onBatchFileResponse,
 	isFollowUpAnswered,
 }: ChatRowContentProps) => {
-	const { t } = useTranslation()
+	const { t, i18n } = useTranslation()
 
 	const { mcpServers, alwaysAllowMcp, currentCheckpoint, mode, apiConfiguration, clineMessages } = useExtensionState()
 	const { info: model } = useSelectedModel(apiConfiguration)
@@ -1099,25 +1099,52 @@ export const ChatRowContent = ({
 								<ErrorRow
 									type="api_failure"
 									message={apiRequestFailedMessage || apiReqStreamingFailedMessage || ""}
-									additionalContent={
-										apiRequestFailedMessage?.toLowerCase().includes("powershell") ? (
-											<>
-												<br />
-												<br />
-												{t("chat:powershell.issues")}{" "}
-												<a
-													href="https://github.com/cline/cline/wiki/TroubleShooting-%E2%80%90-%22PowerShell-is-not-recognized-as-an-internal-or-external-command%22"
-													style={{ color: "inherit", textDecoration: "underline" }}>
-													troubleshooting guide
-												</a>
-												.
-											</>
-										) : undefined
+									docsURL={
+										apiRequestFailedMessage?.toLowerCase().includes("powershell")
+											? "https://github.com/cline/cline/wiki/TroubleShooting-%E2%80%90-%22PowerShell-is-not-recognized-as-an-internal-or-external-command%22"
+											: undefined
 									}
 								/>
 							)}
 						</>
 					)
+				case "api_req_retry_delayed":
+					let body = t(`chat:apiRequest.failed`)
+					let retryInfo, code, docsURL
+					if (message.text !== undefined) {
+						// Try to show richer error message for that code, if available
+						if (parseInt(message.text.substring(0, 3)) >= 400) {
+							code = parseInt(message.text)
+							const stringForError = `chat:apiRequest.errorMessage.${code}`
+							if (i18n.exists(stringForError)) {
+								body = t(stringForError)
+								// Fill this out in upcoming PRs
+								// Do not remove this
+								// switch(code) {
+								// 	case ERROR_CODE:
+								// 		docsURL = ???
+								// 		break;
+								// }
+							} else {
+								body = t("chat:apiRequest.errorMessage.unknown")
+								docsURL = "mailto:[email protected]?subject=Unknown API Error"
+							}
+							retryInfo = (
+								<p className="mt-1 font-light text-xs text-vscode-errorForeground/80 cursor-default">
+									{message.text.substring(4)}
+								</p>
+							)
+						}
+					}
+					return (
+						<ErrorRow
+							type="api_req_retry_delayed"
+							code={code}
+							message={body}
+							docsURL={docsURL}
+							additionalContent={retryInfo}
+						/>
+					)
 				case "api_req_finished":
 					return null // we should never see this message type
 				case "text":
@@ -1258,7 +1285,13 @@ export const ChatRowContent = ({
 					}
 					return message.contextCondense ? <ContextCondenseRow {...message.contextCondense} /> : null
 				case "condense_context_error":
-					return <CondenseContextErrorRow errorText={message.text} />
+					return (
+						<ErrorRow
+							type="error"
+							title={t("chat:contextCondense.errorHeader")}
+							message={message.text || ""}
+						/>
+					)
 				case "codebase_search_result":
 					let parsed: {
 						content: {

+ 2 - 2
webview-ui/src/components/chat/CommandExecutionError.tsx

@@ -12,11 +12,11 @@ export const CommandExecutionError = () => {
 	}, [])
 
 	return (
-		<div className="text-sm bg-vscode-editor-background border border-vscode-border rounded-xs p-2">
+		<div className="text-sm bg-vscode-editor-background border border-vscode-border rounded-lg p-3 ml-6">
 			<div className="flex flex-col gap-2">
 				<div className="flex items-center">
 					<i className="codicon codicon-warning mr-1 text-vscode-editorWarning-foreground" />
-					<span className="text-vscode-editorWarning-foreground font-medium">
+					<span className="text-vscode-editorWarning-foreground font-semibold">
 						{t("chat:shellIntegration.title")}
 					</span>
 				</div>

+ 0 - 13
webview-ui/src/components/chat/ContextCondenseRow.tsx

@@ -66,16 +66,3 @@ export const CondensingContextRow = () => {
 		</div>
 	)
 }
-
-export const CondenseContextErrorRow = ({ errorText }: { errorText?: string }) => {
-	const { t } = useTranslation()
-	return (
-		<div className="flex flex-col gap-1">
-			<div className="flex items-center gap-2">
-				<span className="codicon codicon-warning text-vscode-editorWarning-foreground opacity-80 text-base -mb-0.5"></span>
-				<span className="font-bold text-vscode-foreground">{t("chat:contextCondense.errorHeader")}</span>
-			</div>
-			<span className="text-vscode-descriptionForeground text-sm">{errorText}</span>
-		</div>
-	)
-}

+ 88 - 22
webview-ui/src/components/chat/ErrorRow.tsx

@@ -1,12 +1,57 @@
 import React, { useState, useCallback, memo } from "react"
 import { useTranslation } from "react-i18next"
 import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
-import { MessageCircleWarning } from "lucide-react"
+import { BookOpenText, MessageCircleWarning } from "lucide-react"
 import { useCopyToClipboard } from "@src/utils/clipboard"
+import { vscode } from "@src/utils/vscode"
 import CodeBlock from "../common/CodeBlock"
 
+/**
+ * Unified error display component for all error types in the chat.
+ * Provides consistent styling, icons, and optional documentation links across all errors.
+ *
+ * @param type - Error type determines icon and default title
+ * @param title - Optional custom title (overrides default for error type)
+ * @param message - Error message text (required)
+ * @param docsURL - Optional documentation link URL (shown as "Learn more" with book icon)
+ * @param showCopyButton - Whether to show copy button for error message
+ * @param expandable - Whether error content can be expanded/collapsed
+ * @param defaultExpanded - Whether expandable content starts expanded
+ * @param additionalContent - Optional React nodes to render after message
+ * @param headerClassName - Custom CSS classes for header section
+ * @param messageClassName - Custom CSS classes for message section
+ *
+ * @example
+ * // Simple error
+ * <ErrorRow type="error" message="File not found" />
+ *
+ * @example
+ * // Error with documentation link
+ * <ErrorRow
+ *   type="api_failure"
+ *   message="API key missing"
+ *   docsURL="https://docs.example.com/api-setup"
+ * />
+ *
+ * @example
+ * // Expandable error with code
+ * <ErrorRow
+ *   type="diff_error"
+ *   message="Patch failed to apply"
+ *   expandable={true}
+ *   defaultExpanded={false}
+ *   additionalContent={<pre>{errorDetails}</pre>}
+ * />
+ */
 export interface ErrorRowProps {
-	type: "error" | "mistake_limit" | "api_failure" | "diff_error" | "streaming_failed" | "cancelled"
+	type:
+		| "error"
+		| "mistake_limit"
+		| "api_failure"
+		| "diff_error"
+		| "streaming_failed"
+		| "cancelled"
+		| "api_req_retry_delayed"
 	title?: string
 	message: string
 	showCopyButton?: boolean
@@ -15,6 +60,8 @@ export interface ErrorRowProps {
 	additionalContent?: React.ReactNode
 	headerClassName?: string
 	messageClassName?: string
+	code?: number
+	docsURL?: string // NEW: Optional documentation link
 }
 
 /**
@@ -31,6 +78,8 @@ export const ErrorRow = memo(
 		additionalContent,
 		headerClassName,
 		messageClassName,
+		docsURL,
+		code,
 	}: ErrorRowProps) => {
 		const { t } = useTranslation()
 		const [isExpanded, setIsExpanded] = useState(defaultExpanded)
@@ -48,6 +97,8 @@ export const ErrorRow = memo(
 					return t("chat:troubleMessage")
 				case "api_failure":
 					return t("chat:apiRequest.failed")
+				case "api_req_retry_delayed":
+					return t("chat:apiRequest.errorTitle", { code: code ? ` · ${code}` : "" })
 				case "streaming_failed":
 					return t("chat:apiRequest.streamingFailed")
 				case "cancelled":
@@ -84,17 +135,17 @@ export const ErrorRow = memo(
 		// For diff_error type with expandable content
 		if (type === "diff_error" && expandable) {
 			return (
-				<div className="mt-0 overflow-hidden mb-2">
+				<div className="mt-0 overflow-hidden mb-2 pr-1 group">
 					<div
-						className={`font-normal text-vscode-editor-foreground flex items-center justify-between cursor-pointer ${
-							isExpanded ? "border-b border-vscode-editorGroup-border" : ""
-						}`}
+						className="font-sm text-vscode-editor-foreground flex items-center justify-between cursor-pointer"
 						onClick={handleToggleExpand}>
-						<div className="flex items-center gap-2 flex-grow">
-							<MessageCircleWarning className="w-4 text-vscode-errorForeground" />
-							<span className="font-bold">{errorTitle}</span>
+						<div className="flex items-center gap-2 flex-grow  text-vscode-errorForeground">
+							<MessageCircleWarning className="w-4" />
+							<span className="text-vscode-errorForeground font-bold grow cursor-pointer">
+								{errorTitle}
+							</span>
 						</div>
-						<div className="flex items-center">
+						<div className="flex items-center transition-opacity opacity-0 group-hover:opacity-100">
 							{showCopyButton && (
 								<VSCodeButton
 									appearance="icon"
@@ -107,7 +158,7 @@ export const ErrorRow = memo(
 						</div>
 					</div>
 					{isExpanded && (
-						<div className="p-2 bg-vscode-editor-background border-t-0">
+						<div className="px-2 py-1 mt-2 bg-vscode-editor-background ml-6 rounded-lg">
 							<CodeBlock source={message} language="xml" />
 						</div>
 					)}
@@ -117,21 +168,36 @@ export const ErrorRow = memo(
 
 		// Standard error display
 		return (
-			<>
+			<div className="group pr-2">
 				{errorTitle && (
-					<div className={headerClassName || "flex items-center gap-2 break-words"}>
+					<div className={headerClassName || "flex items-center justify-between gap-2 break-words"}>
 						<MessageCircleWarning className="w-4 text-vscode-errorForeground" />
-						<span className="text-vscode-errorForeground font-bold">{errorTitle}</span>
+						<span className="text-vscode-errorForeground font-bold grow cursor-default">{errorTitle}</span>
+						{docsURL && (
+							<a
+								href={docsURL}
+								className="text-sm flex items-center gap-1 transition-opacity opacity-0 group-hover:opacity-100"
+								onClick={(e) => {
+									e.preventDefault()
+									vscode.postMessage({ type: "openExternal", url: docsURL })
+								}}>
+								<BookOpenText className="size-3 mt-[3px]" />
+								{t("chat:apiRequest.errorMessage.docs")}
+							</a>
+						)}
 					</div>
 				)}
-				<p
-					className={
-						messageClassName || "ml-6 my-0 whitespace-pre-wrap break-words text-vscode-errorForeground"
-					}>
-					{message}
-				</p>
-				{additionalContent}
-			</>
+				<div className="pl-6 py-1">
+					<p
+						className={
+							messageClassName ||
+							"my-0 font-light whitespace-pre-wrap break-words text-vscode-errorForeground"
+						}>
+						{message}
+					</p>
+					{additionalContent}
+				</div>
+			</div>
 		)
 	},
 )

+ 12 - 1
webview-ui/src/i18n/locales/ca/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Sol·licitud API ha fallat",
 		"streaming": "Sol·licitud API...",
 		"cancelled": "Sol·licitud API cancel·lada",
-		"streamingFailed": "Transmissió API ha fallat"
+		"streamingFailed": "Transmissió API ha fallat",
+		"errorTitle": "Error API {{code}}",
+		"errorMessage": {
+			"docs": "Documentació",
+			"400": "El proveïdor no ha pogut processar la sol·licitud tal com es va fer. Interromp la tasca i prova una abordatge diferent.",
+			"401": "No s'ha pogut autenticar amb el proveïdor. Si us plau, verifica la teva configuració de clau API.",
+			"402": "Sembla que se t'han acabat els fons/crèdits al teu compte. Vés al teu proveïdor i afegeix més per continuar.",
+			"403": "No autoritzat. La teva clau API és vàlida, però el proveïdor ha rebutjat completar aquesta sol·licitud.",
+			"429": "Massa sol·licituds. Estàs sent limitat pel proveïdor. Si us plau espera una mica abans de la teva propera crida API.",
+			"500": "Error del servidor del proveïdor. Quelcom va malament del costat del proveïdor, no hi ha res de malament amb la teva sol·licitud.",
+			"unknown": "Error API desconegut. Si us plau contacta amb el suport de Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Punt de control",

+ 12 - 1
webview-ui/src/i18n/locales/de/chat.json

@@ -136,7 +136,18 @@
 		"failed": "API-Anfrage fehlgeschlagen",
 		"streaming": "API-Anfrage...",
 		"cancelled": "API-Anfrage abgebrochen",
-		"streamingFailed": "API-Streaming fehlgeschlagen"
+		"streamingFailed": "API-Streaming fehlgeschlagen",
+		"errorTitle": "API-Fehler {{code}}",
+		"errorMessage": {
+			"docs": "Dokumentation",
+			"400": "Der Anbieter konnte die Anfrage nicht wie gestellt verarbeiten. Beende die Aufgabe und versuche einen anderen Ansatz.",
+			"401": "Authentifizierung beim Anbieter fehlgeschlagen. Bitte überprüfe deine API-Schlüssel-Konfiguration.",
+			"402": "Es sieht so aus, als ob dir die Guthaben/Credits in deinem Konto ausgegangen sind. Gehe zu deinem Anbieter und füge mehr hinzu, um fortzufahren.",
+			"403": "Nicht autorisiert. Dein API-Schlüssel ist gültig, aber der Anbieter hat sich geweigert, diese Anfrage zu erfüllen.",
+			"429": "Zu viele Anfragen. Du wirst vom Anbieter rate-limitiert. Bitte warte ein wenig, bevor du den nächsten API-Aufruf machst.",
+			"500": "Fehler auf dem Server des Anbieters. Es stimmt etwas mit der Anbieterseite nicht, mit deiner Anfrage stimmt alles.",
+			"unknown": "Unbekannter API-Fehler. Bitte kontaktiere den Roo Code Support."
+		}
 	},
 	"checkpoint": {
 		"regular": "Checkpoint",

+ 12 - 1
webview-ui/src/i18n/locales/en/chat.json

@@ -142,7 +142,18 @@
 		"failed": "API Request Failed",
 		"streaming": "API Request...",
 		"cancelled": "API Request Cancelled",
-		"streamingFailed": "API Streaming Failed"
+		"streamingFailed": "API Streaming Failed",
+		"errorTitle": "API Error {{code}}",
+		"errorMessage": {
+			"docs": "Docs",
+			"400": "The provider couldn't process the request as made. Stop the task and try a different approach.",
+			"401": "Couldn't authenticate with provider. Please check your API key configuration.",
+			"402": "You seem to have run out of funds/credits in your account. Go to your provider and add more to continue.",
+			"403": "Unauthorized. Your API key is valid, but the provider refused to complete this request.",
+			"429": "Too many requests. You're being rate-limited by the provider. Please wait a bit before your next API call.",
+			"500": "Provider server error. Something is wrong on the provider side, there's nothing wrong with your request.",
+			"unknown": "Unknown API error. Please contact Roo Code support."
+		}
 	},
 	"checkpoint": {
 		"regular": "Checkpoint",

+ 12 - 1
webview-ui/src/i18n/locales/es/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Solicitud API falló",
 		"streaming": "Solicitud API...",
 		"cancelled": "Solicitud API cancelada",
-		"streamingFailed": "Transmisión API falló"
+		"streamingFailed": "Transmisión API falló",
+		"errorTitle": "Error API {{code}}",
+		"errorMessage": {
+			"docs": "Documentación",
+			"400": "El proveedor no pudo procesar la solicitud tal como se hizo. Detén la tarea e intenta un enfoque diferente.",
+			"401": "No se pudo autenticar con el proveedor. Por favor verifica tu configuración de clave API.",
+			"402": "Parece que se te han acabado los fondos/créditos en tu cuenta. Ve a tu proveedor y agrega más para continuar.",
+			"403": "No autorizado. Tu clave API es válida, pero el proveedor se negó a completar esta solicitud.",
+			"429": "Demasiadas solicitudes. Te estás viendo limitado por el proveedor. Por favor espera un poco antes de tu próxima llamada API.",
+			"500": "Error del servidor del proveedor. Algo está mal en el lado del proveedor, no hay nada mal con tu solicitud.",
+			"unknown": "Error API desconocido. Por favor contacta al soporte de Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Punto de control",

+ 12 - 1
webview-ui/src/i18n/locales/fr/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Échec de la requête API",
 		"streaming": "Requête API...",
 		"cancelled": "Requête API annulée",
-		"streamingFailed": "Échec du streaming API"
+		"streamingFailed": "Échec du streaming API",
+		"errorTitle": "Erreur API {{code}}",
+		"errorMessage": {
+			"docs": "Documentation",
+			"400": "Le fournisseur n'a pas pu traiter la demande telle que présentée. Arrête la tâche et essaie une approche différente.",
+			"401": "Impossible de s'authentifier auprès du fournisseur. Veuillez vérifier la configuration de votre clé API.",
+			"402": "Il semble que vous ayez épuisé vos fonds/crédits sur votre compte. Allez chez votre fournisseur et ajoutez-en plus pour continuer.",
+			"403": "Non autorisé. Votre clé API est valide, mais le fournisseur a refusé de compléter cette demande.",
+			"429": "Trop de demandes. Vous êtes limité par le fournisseur. Veuillez attendre un peu avant votre prochain appel API.",
+			"500": "Erreur du serveur du fournisseur. Quelque chose ne va pas du côté du fournisseur, il n'y a rien de mal avec votre demande.",
+			"unknown": "Erreur API inconnue. Veuillez contacter le support Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Point de contrôle",

+ 12 - 1
webview-ui/src/i18n/locales/hi/chat.json

@@ -136,7 +136,18 @@
 		"failed": "API अनुरोध विफल हुआ",
 		"streaming": "API अनुरोध...",
 		"cancelled": "API अनुरोध रद्द किया गया",
-		"streamingFailed": "API स्ट्रीमिंग विफल हुई"
+		"streamingFailed": "API स्ट्रीमिंग विफल हुई",
+		"errorTitle": "API त्रुटि {{code}}",
+		"errorMessage": {
+			"docs": "डॉक्स",
+			"400": "प्रदाता अनुरोध को जैसे बनाया गया था उसे प्रोसेस नहीं कर सका। कार्य को रोकें और एक अलग तरीका आजमाएं।",
+			"401": "प्रदाता के साथ प्रमाणित नहीं किए जा सके। अपने API कुंजी कॉन्फ़िगरेशन की जाँच करें।",
+			"402": "ऐसा लगता है कि आपके खाते में फंड/क्रेडिट समाप्त हो गए हैं। अपने प्रदाता के पास जाएं और जारी रखने के लिए और अधिक जोड़ें।",
+			"403": "अनुमति नहीं। आपकी API कुंजी वैध है, लेकिन प्रदाता ने इस अनुरोध को पूरा करने से इनकार कर दिया।",
+			"429": "बहुत सारे अनुरोध। आप प्रदाता द्वारा दर-सीमित हो रहे हैं। कृपया अपनी अगली API कॉल से पहले थोड़ी देर प्रतीक्षा करें।",
+			"500": "प्रदाता सर्वर त्रुटि। प्रदाता की ओर से कुछ गलत है, आपके अनुरोध में कुछ गलत नहीं है।",
+			"unknown": "अज्ञात API त्रुटि। कृपया Roo Code सहायता से संपर्क करें।"
+		}
 	},
 	"checkpoint": {
 		"regular": "चेकपॉइंट",

+ 12 - 1
webview-ui/src/i18n/locales/id/chat.json

@@ -145,7 +145,18 @@
 		"failed": "Permintaan API Gagal",
 		"streaming": "Permintaan API...",
 		"cancelled": "Permintaan API Dibatalkan",
-		"streamingFailed": "Streaming API Gagal"
+		"streamingFailed": "Streaming API Gagal",
+		"errorTitle": "Kesalahan API {{code}}",
+		"errorMessage": {
+			"docs": "Dokumentasi",
+			"400": "Penyedia tidak dapat memproses permintaan seperti yang dibuat. Hentikan tugas dan coba pendekatan berbeda.",
+			"401": "Tidak dapat mengautentikasi dengan penyedia. Harap periksa konfigurasi kunci API Anda.",
+			"402": "Tampaknya Anda telah kehabisan dana/kredit di akun Anda. Pergi ke penyedia Anda dan tambahkan lebih banyak untuk melanjutkan.",
+			"403": "Tidak sah. Kunci API Anda valid, tetapi penyedia menolak untuk menyelesaikan permintaan ini.",
+			"429": "Terlalu banyak permintaan. Anda dibatasi tingkat oleh penyedia. Harap tunggu sebentar sebelum panggilan API berikutnya Anda.",
+			"500": "Kesalahan server penyedia. Ada yang salah di sisi penyedia, tidak ada yang salah dengan permintaan Anda.",
+			"unknown": "Kesalahan API yang tidak diketahui. Harap hubungi dukungan Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Checkpoint",

+ 12 - 1
webview-ui/src/i18n/locales/it/chat.json

@@ -139,7 +139,18 @@
 		"failed": "Richiesta API fallita",
 		"streaming": "Richiesta API...",
 		"cancelled": "Richiesta API annullata",
-		"streamingFailed": "Streaming API fallito"
+		"streamingFailed": "Streaming API fallito",
+		"errorTitle": "Errore API {{code}}",
+		"errorMessage": {
+			"docs": "Documentazione",
+			"400": "Il provider non ha potuto elaborare la richiesta come presentata. Interrompi l'attività e prova un approccio diverso.",
+			"401": "Impossibile autenticare con il provider. Verifica la configurazione della tua chiave API.",
+			"402": "Sembra che tu abbia esaurito i fondi/crediti nel tuo account. Vai al tuo provider e aggiungi altro per continuare.",
+			"403": "Non autorizzato. La tua chiave API è valida, ma il provider ha rifiutato di completare questa richiesta.",
+			"429": "Troppe richieste. Sei limitato dal provider. Attendi un po' prima della tua prossima chiamata API.",
+			"500": "Errore del server del provider. C'è qualcosa di sbagliato dal lato del provider, non c'è nulla di sbagliato nella tua richiesta.",
+			"unknown": "Errore API sconosciuto. Contatta il supporto di Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Checkpoint",

+ 12 - 1
webview-ui/src/i18n/locales/ja/chat.json

@@ -136,7 +136,18 @@
 		"failed": "APIリクエスト失敗",
 		"streaming": "APIリクエスト...",
 		"cancelled": "APIリクエストキャンセル",
-		"streamingFailed": "APIストリーミング失敗"
+		"streamingFailed": "APIストリーミング失敗",
+		"errorTitle": "APIエラー {{code}}",
+		"errorMessage": {
+			"docs": "ドキュメント",
+			"400": "プロバイダーはリクエストをそのまま処理できませんでした。タスクを停止して別のアプローチを試してください。",
+			"401": "プロバイダーで認証できませんでした。API キーの設定を確認してください。",
+			"402": "アカウントの資金/クレジットが不足しているようです。プロバイダーに移動してさらに追加してください。",
+			"403": "認可されていません。API キーは有効ですが、プロバイダーがこのリクエストの完了を拒否しました。",
+			"429": "リクエストが多すぎます。プロバイダーによってレート制限されています。次の API 呼び出しの前にお待ちください。",
+			"500": "プロバイダー サーバー エラー。プロバイダー側に問題があり、リクエスト自体に問題はありません。",
+			"unknown": "不明な API エラー。Roo Code のサポートにお問い合わせください。"
+		}
 	},
 	"checkpoint": {
 		"regular": "チェックポイント",

+ 12 - 1
webview-ui/src/i18n/locales/ko/chat.json

@@ -136,7 +136,18 @@
 		"failed": "API 요청 실패",
 		"streaming": "API 요청...",
 		"cancelled": "API 요청 취소됨",
-		"streamingFailed": "API 스트리밍 실패"
+		"streamingFailed": "API 스트리밍 실패",
+		"errorTitle": "API 오류 {{code}}",
+		"errorMessage": {
+			"docs": "문서",
+			"400": "공급자가 요청을 처리할 수 없습니다. 작업을 중지하고 다른 방법을 시도하세요.",
+			"401": "공급자로 인증할 수 없습니다. API 키 구성을 확인하세요.",
+			"402": "계정의 자금/크레딧이 부족한 것 같습니다. 공급자에게 가서 더 추가하세요.",
+			"403": "권한 없음. API 키는 유효하지만 공급자가 이 요청을 완료하기를 거부했습니다.",
+			"429": "너무 많은 요청입니다. 공급자에 의해 요청 제한이 적용되고 있습니다. 다음 API 호출 전에 잠깐 기다려주세요.",
+			"500": "공급자 서버 오류입니다. 공급자 쪽에 문제가 있으며 요청에는 문제가 없습니다.",
+			"unknown": "알 수 없는 API 오류입니다. Roo Code 지원팀에 문의하세요."
+		}
 	},
 	"checkpoint": {
 		"regular": "체크포인트",

+ 12 - 1
webview-ui/src/i18n/locales/nl/chat.json

@@ -131,7 +131,18 @@
 		"failed": "API-verzoek mislukt",
 		"streaming": "API-verzoek...",
 		"cancelled": "API-verzoek geannuleerd",
-		"streamingFailed": "API-streaming mislukt"
+		"streamingFailed": "API-streaming mislukt",
+		"errorTitle": "API-fout {{code}}",
+		"errorMessage": {
+			"docs": "Documentatie",
+			"400": "De provider kon het verzoek niet verwerken zoals ingediend. Stop de taak en probeer een ander benadering.",
+			"401": "Kon niet authenticeren met provider. Controleer je API-sleutelconfiguratie.",
+			"402": "Het lijkt erop dat je funds/credits op je account op zijn. Ga naar je provider en voeg meer toe om door te gaan.",
+			"403": "Niet geautoriseerd. Je API-sleutel is geldig, maar de provider weigerde dit verzoek in te willigen.",
+			"429": "Te veel verzoeken. Je bent rate-gelimiteerd door de provider. Wacht alsjeblieft even voor je volgende API-aanroep.",
+			"500": "Provider-serverfout. Er is iets mis aan de kant van de provider, er is niets mis met je verzoek.",
+			"unknown": "Onbekende API-fout. Neem alsjeblieft contact op met Roo Code-ondersteuning."
+		}
 	},
 	"checkpoint": {
 		"regular": "Checkpoint",

+ 12 - 1
webview-ui/src/i18n/locales/pl/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Zapytanie API nie powiodło się",
 		"streaming": "Zapytanie API...",
 		"cancelled": "Zapytanie API anulowane",
-		"streamingFailed": "Strumieniowanie API nie powiodło się"
+		"streamingFailed": "Strumieniowanie API nie powiodło się",
+		"errorTitle": "Błąd API {{code}}",
+		"errorMessage": {
+			"docs": "Dokumentacja",
+			"400": "Dostawca nie mógł przetworzyć żądania. Zatrzymaj zadanie i spróbuj innego podejścia.",
+			"401": "Nie można uwierzytelnić u dostawcy. Sprawdź konfigurację klucza API.",
+			"402": "Wygląda na to, że wyczerpałeś środki/kredyty na swoim koncie. Przejdź do dostawcy i dodaj więcej, aby kontynuować.",
+			"403": "Brak autoryzacji. Twój klucz API jest ważny, ale dostawca odmówił ukończenia tego żądania.",
+			"429": "Zbyt wiele żądań. Dostawca ogranicza Ci szybkość żądań. Poczekaj chwilę przed następnym wywołaniem API.",
+			"500": "Błąd serwera dostawcy. Po stronie dostawcy coś się nie powiodło, w Twoim żądaniu nie ma nic złego.",
+			"unknown": "Nieznany błąd API. Skontaktuj się z pomocą techniczną Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Punkt kontrolny",

+ 12 - 1
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Requisição API falhou",
 		"streaming": "Requisição API...",
 		"cancelled": "Requisição API cancelada",
-		"streamingFailed": "Streaming API falhou"
+		"streamingFailed": "Streaming API falhou",
+		"errorTitle": "Erro API {{code}}",
+		"errorMessage": {
+			"docs": "Documentação",
+			"400": "O provedor não conseguiu processar a solicitação conforme feita. Interrompa a tarefa e tente uma abordagem diferente.",
+			"401": "Não foi possível autenticar com o provedor. Por favor, verifique a configuração da sua chave API.",
+			"402": "Parece que você ficou sem fundos/créditos em sua conta. Vá ao seu provedor e adicione mais para continuar.",
+			"403": "Não autorizado. Sua chave API é válida, mas o provedor se recusou a concluir esta solicitação.",
+			"429": "Muitas solicitações. Você está sendo limitado pelo provedor. Por favor, aguarde um pouco antes de sua próxima chamada de API.",
+			"500": "Erro do servidor do provedor. Algo está errado do lado do provedor, não há nada de errado com sua solicitação.",
+			"unknown": "Erro de API desconhecido. Por favor, entre em contato com o suporte do Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Ponto de verificação",

+ 12 - 1
webview-ui/src/i18n/locales/ru/chat.json

@@ -131,7 +131,18 @@
 		"failed": "API-запрос не выполнен",
 		"streaming": "API-запрос...",
 		"cancelled": "API-запрос отменен",
-		"streamingFailed": "Ошибка потокового API-запроса"
+		"streamingFailed": "Ошибка потокового API-запроса",
+		"errorTitle": "Ошибка API {{code}}",
+		"errorMessage": {
+			"docs": "Документация",
+			"400": "Провайдер не смог обработать запрос. Остановите задачу и попробуйте другой подход.",
+			"401": "Не удалось аутентифицироваться у провайдера. Проверьте конфигурацию ключа API.",
+			"402": "Похоже, у вас закончились средства/кредиты на вашем счете. Перейдите к провайдеру и пополните счет, чтобы продолжить.",
+			"403": "Без авторизации. Ваш ключ API действителен, но провайдер отказался выполнить этот запрос.",
+			"429": "Слишком много запросов. Провайдер ограничивает частоту ваших запросов. Пожалуйста, подождите немного перед следующим вызовом API.",
+			"500": "Ошибка сервера провайдера. На стороне провайдера что-то пошло не так, с вашим запросом все в порядке.",
+			"unknown": "Неизвестная ошибка API. Пожалуйста, свяжитесь с поддержкой Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Точка сохранения",

+ 12 - 1
webview-ui/src/i18n/locales/tr/chat.json

@@ -136,7 +136,18 @@
 		"failed": "API İsteği Başarısız",
 		"streaming": "API İsteği...",
 		"cancelled": "API İsteği İptal Edildi",
-		"streamingFailed": "API Akışı Başarısız"
+		"streamingFailed": "API Akışı Başarısız",
+		"errorTitle": "API Hatası {{code}}",
+		"errorMessage": {
+			"docs": "Belgeler",
+			"400": "Sağlayıcı isteği bu şekilde işleyemedi. Görevi durdur ve farklı bir yaklaşım dene.",
+			"401": "Sağlayıcı ile kimlik doğrulaması yapılamadı. Lütfen API anahtarı yapılandırmanızı kontrol edin.",
+			"402": "Hesabınızda para/kredi bitti gibi görünüyor. Sağlayıcıya git ve devam etmek için daha fazla ekle.",
+			"403": "Yetkisiz. API anahtarınız geçerli ama sağlayıcı bu isteği tamamlamayı reddetti.",
+			"429": "Çok fazla istek. Sağlayıcı tarafından oran sınırlaması uygulanıyor. Lütfen sonraki API çağrısından önce biraz bekle.",
+			"500": "Sağlayıcı sunucu hatası. Sağlayıcı tarafında bir sorun var, isteğinizde sorun yok.",
+			"unknown": "Bilinmeyen API hatası. Lütfen Roo Code desteğiyle iletişime geç."
+		}
 	},
 	"checkpoint": {
 		"regular": "Kontrol Noktası",

+ 12 - 1
webview-ui/src/i18n/locales/vi/chat.json

@@ -136,7 +136,18 @@
 		"failed": "Yêu cầu API thất bại",
 		"streaming": "Yêu cầu API...",
 		"cancelled": "Yêu cầu API đã hủy",
-		"streamingFailed": "Streaming API thất bại"
+		"streamingFailed": "Streaming API thất bại",
+		"errorTitle": "Lỗi API {{code}}",
+		"errorMessage": {
+			"docs": "Tài liệu",
+			"400": "Nhà cung cấp không thể xử lý yêu cầu theo cách này. Hãy dừng nhiệm vụ và thử một cách tiếp cận khác.",
+			"401": "Không thể xác thực với nhà cung cấp. Vui lòng kiểm tra cấu hình khóa API của bạn.",
+			"402": "Có vẻ như bạn đã hết tiền/tín dụng trong tài khoản. Hãy truy cập nhà cung cấp và thêm tiền để tiếp tục.",
+			"403": "Truy cập bị từ chối. Khóa API của bạn hợp lệ, nhưng nhà cung cấp từ chối hoàn thành yêu cầu này.",
+			"429": "Quá nhiều yêu cầu. Nhà cung cấp đang giới hạn tốc độ yêu cầu của bạn. Vui lòng chờ một chút trước khi gọi API tiếp theo.",
+			"500": "Lỗi máy chủ của nhà cung cấp. Có sự cố ở phía nhà cung cấp, không có gì sai với yêu cầu của bạn.",
+			"unknown": "Lỗi API không xác định. Vui lòng liên hệ hỗ trợ Roo Code."
+		}
 	},
 	"checkpoint": {
 		"regular": "Điểm kiểm tra",

+ 12 - 1
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -136,7 +136,18 @@
 		"failed": "API请求失败",
 		"streaming": "API请求...",
 		"cancelled": "API请求已取消",
-		"streamingFailed": "API流式传输失败"
+		"streamingFailed": "API流式传输失败",
+		"errorTitle": "API 错误 {{code}}",
+		"errorMessage": {
+			"docs": "文档",
+			"400": "提供商无法按此方式处理请求。请停止任务并尝试不同方法。",
+			"401": "无法向提供商进行身份验证。请检查您的 API 密钥配置。",
+			"402": "您的账户余额/积分似乎已用尽。请前往提供商处充值以继续。",
+			"403": "无权限。您的 API 密钥有效,但提供商拒绝完成此请求。",
+			"429": "请求过于频繁。提供商已对您的请求进行速率限制。请在下一次 API 调用前稍候。",
+			"500": "提供商服务器错误。提供商端出现问题,您的请求无问题。",
+			"unknown": "未知 API 错误。请联系 Roo Code 支持。"
+		}
 	},
 	"checkpoint": {
 		"regular": "检查点",

+ 12 - 1
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -142,7 +142,18 @@
 		"failed": "API 請求失敗",
 		"streaming": "正在處理 API 請求...",
 		"cancelled": "API 請求已取消",
-		"streamingFailed": "API 串流處理失敗"
+		"streamingFailed": "API 串流處理失敗",
+		"errorTitle": "API 錯誤 {{code}}",
+		"errorMessage": {
+			"docs": "文件",
+			"400": "提供商無法按照此方式處理請求。請停止工作並嘗試其他方法。",
+			"401": "無法向提供商進行身份驗證。請檢查您的 API 金鑰設定。",
+			"402": "您的帳戶資金/額度似乎已用盡。請前往提供商增加額度以繼續。",
+			"403": "無權存取。您的 API 金鑰有效,但提供商拒絕完成此請求。",
+			"429": "請求次數過多。提供商已對您的請求進行速率限制。請在下一次 API 呼叫前稍候。",
+			"500": "提供商伺服器錯誤。提供商端發生問題,您的請求沒有問題。",
+			"unknown": "未知 API 錯誤。請聯絡 Roo Code 支援。"
+		}
 	},
 	"checkpoint": {
 		"regular": "檢查點",