Skip to content

Commit 749026a

Browse files
authored
ux: Further improve error display (#10692)
* Ensures error details are shown for all errors (except diff, which has its own case) * More details * litellm is a proxy
1 parent 78821a3 commit 749026a

22 files changed

Lines changed: 97 additions & 64 deletions

File tree

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,14 +1132,16 @@ export const ChatRowContent = ({
11321132
// }
11331133
} else {
11341134
body = t("chat:apiRequest.errorMessage.unknown")
1135-
docsURL = "mailto:support@roocode.com?subject=Unknown API Error"
1135+
docsURL =
1136+
"mailto:support@roocode.com?subject=Unknown API Error&body=[Please include full error details]"
11361137
}
11371138
} else if (message.text.indexOf("Connection error") === 0) {
11381139
body = t("chat:apiRequest.errorMessage.connection")
11391140
} else {
11401141
// Non-HTTP-status-code error message - store full text as errorDetails
11411142
body = t("chat:apiRequest.errorMessage.unknown")
1142-
docsURL = "mailto:support@roocode.com?subject=Unknown API Error"
1143+
docsURL =
1144+
"mailto:support@roocode.com?subject=Unknown API Error&body=[Please include full error details]"
11431145
}
11441146
}
11451147

@@ -1336,7 +1338,9 @@ export const ChatRowContent = ({
13361338
}
13371339

13381340
// Fallback for generic errors
1339-
return <ErrorRow type="error" message={message.text || t("chat:error")} />
1341+
return (
1342+
<ErrorRow type="error" message={message.text || t("chat:error")} errorDetails={message.text} />
1343+
)
13401344
case "completion_result":
13411345
return (
13421346
<>
@@ -1526,7 +1530,7 @@ export const ChatRowContent = ({
15261530
case "ask":
15271531
switch (message.ask) {
15281532
case "mistake_limit_reached":
1529-
return <ErrorRow type="mistake_limit" message={message.text || ""} />
1533+
return <ErrorRow type="mistake_limit" message={message.text || ""} errorDetails={message.text} />
15301534
case "command":
15311535
return (
15321536
<CommandExecution

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState, useCallback, memo, useMemo } from "react"
22
import { useTranslation } from "react-i18next"
33
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
4-
import { BookOpenText, MessageCircleWarning, Copy, Check, Microscope } from "lucide-react"
4+
import { BookOpenText, MessageCircleWarning, Copy, Check, Microscope, Info } from "lucide-react"
55

66
import { useCopyToClipboard } from "@src/utils/clipboard"
77
import { vscode } from "@src/utils/vscode"
@@ -10,12 +10,13 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "
1010
import { Button } from "../ui"
1111
import { useExtensionState } from "@src/context/ExtensionStateContext"
1212
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
13+
import { PROVIDERS } from "../settings/constants"
1314

1415
/**
1516
* Unified error display component for all error types in the chat.
1617
* Provides consistent styling, icons, and optional documentation links across all errors.
1718
*
18-
* @param type - Error type determines icon and default title
19+
* @param type - Error type determines default title
1920
* @param title - Optional custom title (overrides default for error type)
2021
* @param message - Error message text (required)
2122
* @param docsURL - Optional documentation link URL (shown as "Learn more" with book icon)
@@ -97,21 +98,23 @@ export const ErrorRow = memo(
9798
const { version, apiConfiguration } = useExtensionState()
9899
const { provider, id: modelId } = useSelectedModel(apiConfiguration)
99100

101+
const usesProxy = PROVIDERS.find((p) => p.value === provider)?.proxy ?? false
102+
100103
// Format error details with metadata prepended
101104
const formattedErrorDetails = useMemo(() => {
102105
if (!errorDetails) return undefined
103106

104107
const metadata = [
105108
`Date/time: ${new Date().toISOString()}`,
106109
`Extension version: ${version}`,
107-
`Provider: ${provider}`,
110+
`Provider: ${provider}${usesProxy ? " (proxy)" : ""}`,
108111
`Model: ${modelId}`,
109112
"",
110113
"",
111114
].join("\n")
112115

113116
return metadata + errorDetails
114-
}, [errorDetails, version, provider, modelId])
117+
}, [errorDetails, version, provider, modelId, usesProxy])
115118

116119
const handleDownloadDiagnostics = useCallback(
117120
(e: React.MouseEvent) => {
@@ -290,10 +293,18 @@ export const ErrorRow = memo(
290293
<DialogHeader>
291294
<DialogTitle>{t("chat:errorDetails.title")}</DialogTitle>
292295
</DialogHeader>
293-
<div className="max-h-96 overflow-auto px-3 bg-vscode-editor-background rounded-xl border border-vscode-editorGroup-border">
294-
<pre className="font-mono text-sm whitespace-pre-wrap break-words bg-transparent">
296+
<div className="max-h-96 overflow-auto bg-vscode-editor-background rounded-xl border border-vscode-editorGroup-border">
297+
<pre className="font-mono text-sm whitespace-pre-wrap break-words bg-transparent px-3">
295298
{formattedErrorDetails}
296299
</pre>
300+
{usesProxy && (
301+
<div className="cursor-default flex gap-2 border-t-1 px-3 py-2 border-vscode-editorGroup-border bg-foreground/5 text-vscode-button-secondaryForeground">
302+
<Info className="size-3 shrink-0 mt-1 text-vscode-descriptionForeground" />
303+
<span className="text-vscode-descriptionForeground text-sm">
304+
{t("chat:errorDetails.proxyProvider")}
305+
</span>
306+
</div>
307+
)}
297308
</div>
298309
<DialogFooter>
299310
<Button variant="secondary" className="w-full" onClick={handleCopyDetails}>

webview-ui/src/components/settings/__tests__/ApiOptions.provider-filtering.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ describe("ApiOptions Provider Filtering", () => {
128128
;(MODELS_BY_PROVIDER as any).emptyProvider = {}
129129

130130
// Add the empty provider to PROVIDERS
131-
PROVIDERS.push({ value: "emptyProvider", label: "Empty Provider" })
131+
PROVIDERS.push({ value: "emptyProvider", label: "Empty Provider", proxy: false })
132132

133133
renderWithProviders()
134134

@@ -235,7 +235,7 @@ describe("ApiOptions Provider Filtering", () => {
235235
// Add an empty static provider to test
236236
;(MODELS_BY_PROVIDER as any).testEmptyProvider = {}
237237
// Add the provider to the PROVIDERS list
238-
PROVIDERS.push({ value: "testEmptyProvider", label: "Test Empty Provider" })
238+
PROVIDERS.push({ value: "testEmptyProvider", label: "Test Empty Provider", proxy: false })
239239

240240
// Create a mock organization allow list that allows the provider but no models
241241
const allowList: OrganizationAllowList = {

webview-ui/src/components/settings/constants.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -47,38 +47,38 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
4747
}
4848

4949
export const PROVIDERS = [
50-
{ value: "openrouter", label: "OpenRouter" },
51-
{ value: "deepinfra", label: "DeepInfra" },
52-
{ value: "anthropic", label: "Anthropic" },
53-
{ value: "claude-code", label: "Claude Code" },
54-
{ value: "cerebras", label: "Cerebras" },
55-
{ value: "gemini", label: "Google Gemini" },
56-
{ value: "doubao", label: "Doubao" },
57-
{ value: "deepseek", label: "DeepSeek" },
58-
{ value: "moonshot", label: "Moonshot" },
59-
{ value: "openai-native", label: "OpenAI" },
60-
{ value: "openai", label: "OpenAI Compatible" },
61-
{ value: "qwen-code", label: "Qwen Code" },
62-
{ value: "vertex", label: "GCP Vertex AI" },
63-
{ value: "bedrock", label: "Amazon Bedrock" },
64-
{ value: "vscode-lm", label: "VS Code LM API" },
65-
{ value: "mistral", label: "Mistral" },
66-
{ value: "lmstudio", label: "LM Studio" },
67-
{ value: "ollama", label: "Ollama" },
68-
{ value: "unbound", label: "Unbound" },
69-
{ value: "requesty", label: "Requesty" },
70-
{ value: "xai", label: "xAI (Grok)" },
71-
{ value: "groq", label: "Groq" },
72-
{ value: "huggingface", label: "Hugging Face" },
73-
{ value: "chutes", label: "Chutes AI" },
74-
{ value: "litellm", label: "LiteLLM" },
75-
{ value: "sambanova", label: "SambaNova" },
76-
{ value: "zai", label: "Z.ai" },
77-
{ value: "fireworks", label: "Fireworks AI" },
78-
{ value: "featherless", label: "Featherless AI" },
79-
{ value: "io-intelligence", label: "IO Intelligence" },
80-
{ value: "roo", label: "Roo Code Router" },
81-
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway" },
82-
{ value: "minimax", label: "MiniMax" },
83-
{ value: "baseten", label: "Baseten" },
50+
{ value: "openrouter", label: "OpenRouter", proxy: false },
51+
{ value: "deepinfra", label: "DeepInfra", proxy: false },
52+
{ value: "anthropic", label: "Anthropic", proxy: false },
53+
{ value: "claude-code", label: "Claude Code", proxy: false },
54+
{ value: "cerebras", label: "Cerebras", proxy: false },
55+
{ value: "gemini", label: "Google Gemini", proxy: false },
56+
{ value: "doubao", label: "Doubao", proxy: false },
57+
{ value: "deepseek", label: "DeepSeek", proxy: false },
58+
{ value: "moonshot", label: "Moonshot", proxy: false },
59+
{ value: "openai-native", label: "OpenAI", proxy: false },
60+
{ value: "openai", label: "OpenAI Compatible", proxy: true },
61+
{ value: "qwen-code", label: "Qwen Code", proxy: false },
62+
{ value: "vertex", label: "GCP Vertex AI", proxy: false },
63+
{ value: "bedrock", label: "Amazon Bedrock", proxy: false },
64+
{ value: "vscode-lm", label: "VS Code LM API", proxy: false },
65+
{ value: "mistral", label: "Mistral", proxy: false },
66+
{ value: "lmstudio", label: "LM Studio", proxy: true },
67+
{ value: "ollama", label: "Ollama", proxy: true },
68+
{ value: "unbound", label: "Unbound", proxy: false },
69+
{ value: "requesty", label: "Requesty", proxy: false },
70+
{ value: "xai", label: "xAI (Grok)", proxy: false },
71+
{ value: "groq", label: "Groq", proxy: false },
72+
{ value: "huggingface", label: "Hugging Face", proxy: false },
73+
{ value: "chutes", label: "Chutes AI", proxy: false },
74+
{ value: "litellm", label: "LiteLLM", proxy: true },
75+
{ value: "sambanova", label: "SambaNova", proxy: false },
76+
{ value: "zai", label: "Z.ai", proxy: false },
77+
{ value: "fireworks", label: "Fireworks AI", proxy: false },
78+
{ value: "featherless", label: "Featherless AI", proxy: false },
79+
{ value: "io-intelligence", label: "IO Intelligence", proxy: false },
80+
{ value: "roo", label: "Roo Code Router", proxy: false },
81+
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway", proxy: false },
82+
{ value: "minimax", label: "MiniMax", proxy: false },
83+
{ value: "baseten", label: "Baseten", proxy: false },
8484
].sort((a, b) => a.label.localeCompare(b.label))

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

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@
303303
"title": "Error Details",
304304
"copyToClipboard": "Copy basic error info",
305305
"copied": "Copied!",
306-
"diagnostics": "Get detailed error info"
306+
"diagnostics": "Get detailed error info",
307+
"proxyProvider": "You seem to be using a proxy-based provider. Make sure to check its logs and to ensure it's not rewriting Roo's requests."
307308
},
308309
"diffError": {
309310
"title": "Edit Unsuccessful"

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

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)