Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 8 additions & 4 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1132,14 +1132,16 @@ export const ChatRowContent = ({
// }
} else {
body = t("chat:apiRequest.errorMessage.unknown")
docsURL = "mailto:support@roocode.com?subject=Unknown API Error"
docsURL =
"mailto:support@roocode.com?subject=Unknown API Error&body=[Please include full error details]"
}
} else if (message.text.indexOf("Connection error") === 0) {
body = t("chat:apiRequest.errorMessage.connection")
} else {
// Non-HTTP-status-code error message - store full text as errorDetails
body = t("chat:apiRequest.errorMessage.unknown")
docsURL = "mailto:support@roocode.com?subject=Unknown API Error"
docsURL =
"mailto:support@roocode.com?subject=Unknown API Error&body=[Please include full error details]"
}
}

Expand Down Expand Up @@ -1336,7 +1338,9 @@ export const ChatRowContent = ({
}

// Fallback for generic errors
return <ErrorRow type="error" message={message.text || t("chat:error")} />
return (
<ErrorRow type="error" message={message.text || t("chat:error")} errorDetails={message.text} />
)
case "completion_result":
return (
<>
Expand Down Expand Up @@ -1526,7 +1530,7 @@ export const ChatRowContent = ({
case "ask":
switch (message.ask) {
case "mistake_limit_reached":
return <ErrorRow type="mistake_limit" message={message.text || ""} />
return <ErrorRow type="mistake_limit" message={message.text || ""} errorDetails={message.text} />
case "command":
return (
<CommandExecution
Expand Down
23 changes: 17 additions & 6 deletions webview-ui/src/components/chat/ErrorRow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useCallback, memo, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { BookOpenText, MessageCircleWarning, Copy, Check, Microscope } from "lucide-react"
import { BookOpenText, MessageCircleWarning, Copy, Check, Microscope, Info } from "lucide-react"

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

/**
* 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 type - Error type determines 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)
Expand Down Expand Up @@ -97,21 +98,23 @@ export const ErrorRow = memo(
const { version, apiConfiguration } = useExtensionState()
const { provider, id: modelId } = useSelectedModel(apiConfiguration)

const usesProxy = PROVIDERS.find((p) => p.value === provider)?.proxy ?? false

// Format error details with metadata prepended
const formattedErrorDetails = useMemo(() => {
if (!errorDetails) return undefined

const metadata = [
`Date/time: ${new Date().toISOString()}`,
`Extension version: ${version}`,
`Provider: ${provider}`,
`Provider: ${provider}${usesProxy ? " (proxy)" : ""}`,
`Model: ${modelId}`,
"",
"",
].join("\n")

return metadata + errorDetails
}, [errorDetails, version, provider, modelId])
}, [errorDetails, version, provider, modelId, usesProxy])

const handleDownloadDiagnostics = useCallback(
(e: React.MouseEvent) => {
Expand Down Expand Up @@ -290,10 +293,18 @@ export const ErrorRow = memo(
<DialogHeader>
<DialogTitle>{t("chat:errorDetails.title")}</DialogTitle>
</DialogHeader>
<div className="max-h-96 overflow-auto px-3 bg-vscode-editor-background rounded-xl border border-vscode-editorGroup-border">
<pre className="font-mono text-sm whitespace-pre-wrap break-words bg-transparent">
<div className="max-h-96 overflow-auto bg-vscode-editor-background rounded-xl border border-vscode-editorGroup-border">
<pre className="font-mono text-sm whitespace-pre-wrap break-words bg-transparent px-3">
{formattedErrorDetails}
</pre>
{usesProxy && (
<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">
<Info className="size-3 shrink-0 mt-1 text-vscode-descriptionForeground" />
<span className="text-vscode-descriptionForeground text-sm">
{t("chat:errorDetails.proxyProvider")}
</span>
</div>
)}
</div>
<DialogFooter>
<Button variant="secondary" className="w-full" onClick={handleCopyDetails}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe("ApiOptions Provider Filtering", () => {
;(MODELS_BY_PROVIDER as any).emptyProvider = {}

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

renderWithProviders()

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

// Create a mock organization allow list that allows the provider but no models
const allowList: OrganizationAllowList = {
Expand Down
68 changes: 34 additions & 34 deletions webview-ui/src/components/settings/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,38 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
}

export const PROVIDERS = [
{ value: "openrouter", label: "OpenRouter" },
{ value: "deepinfra", label: "DeepInfra" },
{ value: "anthropic", label: "Anthropic" },
{ value: "claude-code", label: "Claude Code" },
{ value: "cerebras", label: "Cerebras" },
{ value: "gemini", label: "Google Gemini" },
{ value: "doubao", label: "Doubao" },
{ value: "deepseek", label: "DeepSeek" },
{ value: "moonshot", label: "Moonshot" },
{ value: "openai-native", label: "OpenAI" },
{ value: "openai", label: "OpenAI Compatible" },
{ value: "qwen-code", label: "Qwen Code" },
{ value: "vertex", label: "GCP Vertex AI" },
{ value: "bedrock", label: "Amazon Bedrock" },
{ value: "vscode-lm", label: "VS Code LM API" },
{ value: "mistral", label: "Mistral" },
{ value: "lmstudio", label: "LM Studio" },
{ value: "ollama", label: "Ollama" },
{ value: "unbound", label: "Unbound" },
{ value: "requesty", label: "Requesty" },
{ value: "xai", label: "xAI (Grok)" },
{ value: "groq", label: "Groq" },
{ value: "huggingface", label: "Hugging Face" },
{ value: "chutes", label: "Chutes AI" },
{ value: "litellm", label: "LiteLLM" },
{ value: "sambanova", label: "SambaNova" },
{ value: "zai", label: "Z.ai" },
{ value: "fireworks", label: "Fireworks AI" },
{ value: "featherless", label: "Featherless AI" },
{ value: "io-intelligence", label: "IO Intelligence" },
{ value: "roo", label: "Roo Code Router" },
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway" },
{ value: "minimax", label: "MiniMax" },
{ value: "baseten", label: "Baseten" },
{ value: "openrouter", label: "OpenRouter", proxy: false },
{ value: "deepinfra", label: "DeepInfra", proxy: false },
{ value: "anthropic", label: "Anthropic", proxy: false },
{ value: "claude-code", label: "Claude Code", proxy: false },
{ value: "cerebras", label: "Cerebras", proxy: false },
{ value: "gemini", label: "Google Gemini", proxy: false },
{ value: "doubao", label: "Doubao", proxy: false },
{ value: "deepseek", label: "DeepSeek", proxy: false },
{ value: "moonshot", label: "Moonshot", proxy: false },
{ value: "openai-native", label: "OpenAI", proxy: false },
{ value: "openai", label: "OpenAI Compatible", proxy: true },
{ value: "qwen-code", label: "Qwen Code", proxy: false },
{ value: "vertex", label: "GCP Vertex AI", proxy: false },
{ value: "bedrock", label: "Amazon Bedrock", proxy: false },
{ value: "vscode-lm", label: "VS Code LM API", proxy: false },
{ value: "mistral", label: "Mistral", proxy: false },
{ value: "lmstudio", label: "LM Studio", proxy: true },
{ value: "ollama", label: "Ollama", proxy: true },
{ value: "unbound", label: "Unbound", proxy: false },
{ value: "requesty", label: "Requesty", proxy: false },
{ value: "xai", label: "xAI (Grok)", proxy: false },
{ value: "groq", label: "Groq", proxy: false },
{ value: "huggingface", label: "Hugging Face", proxy: false },
{ value: "chutes", label: "Chutes AI", proxy: false },
{ value: "litellm", label: "LiteLLM", proxy: true },
{ value: "sambanova", label: "SambaNova", proxy: false },
{ value: "zai", label: "Z.ai", proxy: false },
{ value: "fireworks", label: "Fireworks AI", proxy: false },
{ value: "featherless", label: "Featherless AI", proxy: false },
{ value: "io-intelligence", label: "IO Intelligence", proxy: false },
{ value: "roo", label: "Roo Code Router", proxy: false },
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway", proxy: false },
{ value: "minimax", label: "MiniMax", proxy: false },
{ value: "baseten", label: "Baseten", proxy: false },
].sort((a, b) => a.label.localeCompare(b.label))
3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/ca/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/de/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/en/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@
"title": "Error Details",
"copyToClipboard": "Copy basic error info",
"copied": "Copied!",
"diagnostics": "Get detailed error info"
"diagnostics": "Get detailed error info",
"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."
},
"diffError": {
"title": "Edit Unsuccessful"
Expand Down
3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/es/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/fr/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/hi/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/id/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/it/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/ja/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/ko/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/nl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/pl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/pt-BR/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/ru/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/tr/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/vi/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/zh-CN/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion webview-ui/src/i18n/locales/zh-TW/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading