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
This commit is contained in:
Bruno Bergher 2026-01-13 15:43:51 +00:00 committed by GitHub
parent 78821a3951
commit 749026a44b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 97 additions and 64 deletions

View file

@ -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]"
}
}
@ -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 (
<>
@ -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

View file

@ -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"
@ -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)
@ -97,6 +98,8 @@ 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
@ -104,14 +107,14 @@ export const ErrorRow = memo(
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) => {
@ -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}>

View file

@ -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()
@ -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 = {

View file

@ -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))

View file

@ -276,7 +276,8 @@
"title": "Detalls de l'error",
"copyToClipboard": "Copiar al porta-retalls",
"copied": "Copiat!",
"diagnostics": "Obtenir informació d'error detallada"
"diagnostics": "Obtenir informació d'error detallada",
"proxyProvider": "Sembla que estàs utilitzant un proveïdor basat en proxy. Assegura't de comprovar els seus registres i de garantir que no estàs reescrivint les sol·licituds de Roo."
},
"powershell": {
"issues": "Sembla que estàs tenint problemes amb Windows PowerShell, si us plau consulta aquesta documentació per a més informació."

View file

@ -276,7 +276,8 @@
"title": "Fehlerdetails",
"copyToClipboard": "In Zwischenablage kopieren",
"copied": "Kopiert!",
"diagnostics": "Detaillierte Fehlerinformationen abrufen"
"diagnostics": "Detaillierte Fehlerinformationen abrufen",
"proxyProvider": "Du scheinst einen Proxy-basierten Anbieter zu verwenden. Überprüfe unbedingt seine Protokolle und stelle sicher, dass er Roos Anfragen nicht umschreibt."
},
"powershell": {
"issues": "Es scheint, dass du Probleme mit Windows PowerShell hast, bitte sieh dir dies an"

View file

@ -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"

View file

@ -276,7 +276,8 @@
"title": "Detalles del error",
"copyToClipboard": "Copiar al portapapeles",
"copied": "¡Copiado!",
"diagnostics": "Obtener información de error detallada"
"diagnostics": "Obtener información de error detallada",
"proxyProvider": "Parece que estás usando un proveedor basado en proxy. Asegúrate de revisar sus registros y de que no está reescribiendo las solicitudes de Roo."
},
"powershell": {
"issues": "Parece que estás teniendo problemas con Windows PowerShell, por favor consulta esta"

View file

@ -276,7 +276,8 @@
"title": "Détails de l'erreur",
"copyToClipboard": "Copier dans le presse-papiers",
"copied": "Copié !",
"diagnostics": "Obtenir les détails de l'erreur"
"diagnostics": "Obtenir les détails de l'erreur",
"proxyProvider": "Il semble que tu utilises un fournisseur basé sur un proxy. Assure-toi de vérifier ses journaux et de t'assurer qu'il ne réécrit pas les demandes de Roo."
},
"powershell": {
"issues": "Il semble que vous rencontriez des problèmes avec Windows PowerShell, veuillez consulter ce"

View file

@ -276,7 +276,8 @@
"title": "त्रुटि विवरण",
"copyToClipboard": "क्लिपबोर्ड पर कॉपी करें",
"copied": "कॉपी किया गया!",
"diagnostics": "विस्तृत त्रुटि जानकारी प्राप्त करें"
"diagnostics": "विस्तृत त्रुटि जानकारी प्राप्त करें",
"proxyProvider": "ऐसा लगता है कि आप एक प्रॉक्सी-आधारित प्रदाता का उपयोग कर रहे हैं। इसके लॉग की जांच करना सुनिश्चित करें और सुनिश्चित करें कि यह Roo के अनुरोधों को नहीं फिर से लिख रहा है।"
},
"powershell": {
"issues": "ऐसा लगता है कि आपको Windows PowerShell के साथ समस्याएँ हो रही हैं, कृपया इसे देखें"

View file

@ -312,7 +312,8 @@
"title": "Detail Kesalahan",
"copyToClipboard": "Salin ke Clipboard",
"copied": "Disalin!",
"diagnostics": "Dapatkan informasi kesalahan terperinci"
"diagnostics": "Dapatkan informasi kesalahan terperinci",
"proxyProvider": "Tampaknya kamu menggunakan penyedia berbasis proxy. Pastikan untuk memeriksa log-nya dan memastikan tidak menulis ulang permintaan Roo."
},
"powershell": {
"issues": "Sepertinya kamu mengalami masalah Windows PowerShell, silakan lihat ini"

View file

@ -276,7 +276,8 @@
"title": "Dettagli errore",
"copyToClipboard": "Copia negli appunti",
"copied": "Copiato!",
"diagnostics": "Ottieni informazioni errore dettagliate"
"diagnostics": "Ottieni informazioni errore dettagliate",
"proxyProvider": "Sembra che tu stia utilizzando un provider basato su proxy. Assicurati di controllarne i log e di verificare che non stia riscrivendo le richieste di Roo."
},
"powershell": {
"issues": "Sembra che tu stia avendo problemi con Windows PowerShell, consulta questa"

View file

@ -276,7 +276,8 @@
"title": "エラー詳細",
"copyToClipboard": "クリップボードにコピー",
"copied": "コピーしました!",
"diagnostics": "詳細なエラー情報を取得"
"diagnostics": "詳細なエラー情報を取得",
"proxyProvider": "プロキシベースのプロバイダーを使用しているようです。ログを確認し、Rooのリクエストを書き換えていないことを確認してください。"
},
"powershell": {
"issues": "Windows PowerShellに問題があるようです。こちらを参照してください"

View file

@ -276,7 +276,8 @@
"title": "오류 세부 정보",
"copyToClipboard": "클립보드에 복사",
"copied": "복사됨!",
"diagnostics": "상세한 오류 정보 가져오기"
"diagnostics": "상세한 오류 정보 가져오기",
"proxyProvider": "프록시 기반 제공자를 사용하는 것 같습니다. 로그를 확인하고 Roo의 요청을 다시 작성하지 않는지 확인하세요."
},
"powershell": {
"issues": "Windows PowerShell에 문제가 있는 것 같습니다. 다음을 참조하세요"

View file

@ -276,7 +276,8 @@
"title": "Foutdetails",
"copyToClipboard": "Naar klembord kopiëren",
"copied": "Gekopieerd!",
"diagnostics": "Gedetailleerde foutinformatie ophalen"
"diagnostics": "Gedetailleerde foutinformatie ophalen",
"proxyProvider": "Je lijkt een proxy-gebaseerde provider te gebruiken. Zorg ervoor dat je de logs controleert en dat het geen Roo-verzoeken herschrijft."
},
"powershell": {
"issues": "Het lijkt erop dat je problemen hebt met Windows PowerShell, zie deze"

View file

@ -276,7 +276,8 @@
"title": "Szczegóły błędu",
"copyToClipboard": "Kopiuj do schowka",
"copied": "Skopiowano!",
"diagnostics": "Uzyskaj szczegółowe informacje o błędzie"
"diagnostics": "Uzyskaj szczegółowe informacje o błędzie",
"proxyProvider": "Wygląda na to, że używasz dostawcy opartego na proxy. Pamiętaj, aby sprawdzić jego dzienniki i upewnić się, że nie przepisuje żądań Roo."
},
"powershell": {
"issues": "Wygląda na to, że masz problemy z Windows PowerShell, proszę zapoznaj się z tym"

View file

@ -276,7 +276,8 @@
"title": "Detalhes do erro",
"copyToClipboard": "Copiar para área de transferência",
"copied": "Copiado!",
"diagnostics": "Obter informações detalhadas do erro"
"diagnostics": "Obter informações detalhadas do erro",
"proxyProvider": "Parece que você está usando um provedor baseado em proxy. Certifique-se de verificar seus registros e garantir que não está reescrevendo as solicitações do Roo."
},
"powershell": {
"issues": "Parece que você está tendo problemas com o Windows PowerShell, por favor veja este"

View file

@ -277,7 +277,8 @@
"title": "Детали ошибки",
"copyToClipboard": "Скопировать в буфер обмена",
"copied": "Скопировано!",
"diagnostics": "Получить подробную информацию об ошибке"
"diagnostics": "Получить подробную информацию об ошибке",
"proxyProvider": "Похоже, ты используешь провайдера на основе прокси. Убедись, что ты проверил его логи и что он не переписывает запросы Roo."
},
"powershell": {
"issues": "Похоже, у вас проблемы с Windows PowerShell, пожалуйста, ознакомьтесь с этим"

View file

@ -277,7 +277,8 @@
"title": "Hata Detayları",
"copyToClipboard": "Panoya Kopyala",
"copied": "Kopyalandı!",
"diagnostics": "Ayrıntılı hata bilgisi al"
"diagnostics": "Ayrıntılı hata bilgisi al",
"proxyProvider": "Proxy tabanlı bir sağlayıcı kullanıyor gibi görünüyorsun. Günlüklerini kontrol ettiğinden ve Roo'nun isteklerini yeniden yazmadığından emin ol."
},
"powershell": {
"issues": "Windows PowerShell ile ilgili sorunlar yaşıyor gibi görünüyorsunuz, lütfen şu konuya bakın"

View file

@ -277,7 +277,8 @@
"title": "Chi tiết lỗi",
"copyToClipboard": "Sao chép vào clipboard",
"copied": "Đã sao chép!",
"diagnostics": "Nhận thông tin lỗi chi tiết"
"diagnostics": "Nhận thông tin lỗi chi tiết",
"proxyProvider": "Có vẻ bạn đang sử dụng nhà cung cấp dựa trên proxy. Hãy chắc chắn kiểm tra các nhật ký của nó và đảm bảo nó không viết lại các yêu cầu của Roo."
},
"powershell": {
"issues": "Có vẻ như bạn đang gặp vấn đề với Windows PowerShell, vui lòng xem"

View file

@ -277,7 +277,8 @@
"title": "错误详情",
"copyToClipboard": "复制到剪贴板",
"copied": "已复制!",
"diagnostics": "获取详细错误信息"
"diagnostics": "获取详细错误信息",
"proxyProvider": "看起来你在使用基于代理的提供商。请检查日志并确保它不会重写 Roo 的请求。"
},
"powershell": {
"issues": "看起来您遇到了Windows PowerShell问题请参阅此"

View file

@ -310,7 +310,8 @@
"title": "錯誤詳細資訊",
"copyToClipboard": "複製到剪貼簿",
"copied": "已複製!",
"diagnostics": "取得詳細錯誤資訊"
"diagnostics": "取得詳細錯誤資訊",
"proxyProvider": "看起來你在使用基於代理的提供商。請檢查日誌並確保它不會重寫 Roo 的請求。"
},
"powershell": {
"issues": "您似乎遇到了 Windows PowerShell 的問題,請參閱此說明文件"