fix: handle Roo provider authentication errors gracefully

- Add RooAuthenticationError custom error class for specific error handling
- Pre-check Roo authentication before task creation to prevent UI lock-up
- Send taskCreationFailed message to unlock UI when auth fails
- Add translated error messages and button labels for all 18 locales
- Show user-friendly dialog with Sign In and Settings options
- Prevent UI from getting stuck when Roo provider is selected without authentication

This ensures users can switch providers or sign in when Roo authentication fails,
preventing the stuck state that occurred when selecting Roo without being logged in.
This commit is contained in:
daniel-lxs 2025-08-21 14:00:51 -05:00 committed by Roo Code
parent 4216618c72
commit 64c79e6f75
23 changed files with 227 additions and 21 deletions

View file

@ -4,6 +4,14 @@ import type { ProviderSettings, ModelInfo } from "@roo-code/types"
import { ApiStream } from "./transform/stream"
// Custom error class for Roo authentication failures
export class RooAuthenticationError extends Error {
constructor(message: string) {
super(message)
this.name = "RooAuthenticationError"
}
}
import {
GlamaHandler,
AnthropicHandler,
@ -143,7 +151,15 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
case "io-intelligence":
return new IOIntelligenceHandler(options)
case "roo":
return new RooHandler(options)
try {
return new RooHandler(options)
} catch (error) {
// If Roo authentication fails, throw a specific error that can be caught upstream
// This allows proper handling in the UI rather than silently falling back
const errorMessage = error instanceof Error ? error.message : "Authentication required for Roo provider"
console.error("[API] Roo provider authentication failed:", errorMessage)
throw new RooAuthenticationError(errorMessage)
}
case "featherless":
return new FeatherlessHandler(options)
default:

View file

@ -75,7 +75,7 @@ import { setPanel } from "../../activate/registerCommands"
import { t } from "../../i18n"
import { buildApiHandler } from "../../api"
import { buildApiHandler, RooAuthenticationError } from "../../api"
import { forceFullModelDetailsLoad, hasLoadedFullDetails } from "../../api/providers/fetchers/lmstudio"
import { ContextProxy } from "../config/ContextProxy"
@ -776,6 +776,44 @@ export class ClineProvider
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
}
// Check if Roo provider is selected and not authenticated BEFORE creating the task
if (apiConfiguration.apiProvider === "roo") {
try {
// Try to build the API handler to check if authentication works
buildApiHandler(apiConfiguration)
} catch (error) {
if (error instanceof RooAuthenticationError) {
// Show error message to user
const signInText = t("common:mdm.buttons.signIn")
const settingsText = t("common:mdm.buttons.settings")
const selection = await vscode.window.showErrorMessage(
t("common:errors.roo.authenticationRequiredWithOptions"),
signInText,
settingsText,
)
if (selection === signInText) {
// Navigate to account view
await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" })
} else if (selection === settingsText) {
// Open settings to allow provider change
await this.postMessageToWebview({ type: "action", action: "switchTab", tab: "settings" })
}
// Return the current task if it exists to prevent UI issues
const currentTask = this.getCurrentTask()
if (currentTask) {
return currentTask
}
// Throw a specific error that the webview can handle
throw new RooAuthenticationError("Authentication required for Roo provider")
}
// Re-throw other errors
throw error
}
}
const task = new Task({
provider: this,
apiConfiguration,

View file

@ -288,7 +288,38 @@ export const webviewMessageHandler = async (
// Initializing new instance of Cline will make sure that any
// agentically running promises in old instance don't affect our new
// task. This essentially creates a fresh slate for the new task.
await provider.createTask(message.text, message.images)
try {
await provider.createTask(message.text, message.images)
// Task created successfully - notify the UI to reset
await provider.postMessageToWebview({
type: "invoke",
invoke: "newChat",
})
} catch (error) {
// Check if it's a RooAuthenticationError by checking the error name
// The RooAuthenticationError class sets error.name = 'RooAuthenticationError'
if (error instanceof Error && error.name === "RooAuthenticationError") {
// The error has already been handled in createTask with a user-friendly dialog
// Send a specific message to unlock the UI without resetting the chat
provider.log(`Task creation cancelled due to Roo authentication: ${error.message}`)
await provider.postMessageToWebview({
type: "taskCreationFailed",
reason: "rooAuthenticationError",
})
// Exit without sending newChat to keep the chat state
break
}
// For all other errors, reset the UI and show error
await provider.postMessageToWebview({
type: "invoke",
invoke: "newChat",
})
// Show error to user
vscode.window.showErrorMessage(
`Failed to create task: ${error instanceof Error ? error.message : String(error)}`,
)
}
break
case "customInstructions":
await provider.updateCustomInstructions(message.text)

View file

@ -105,7 +105,8 @@
"completionError": "Error de finalització de Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "El proveïdor Roo requereix autenticació al núvol. Si us plau, inicieu sessió a Roo Code Cloud."
"authenticationRequired": "El proveïdor Roo requereix autenticació al núvol. Si us plau, inicieu sessió a Roo Code Cloud.",
"authenticationRequiredWithOptions": "El proveïdor Roo requereix autenticació. Si us plau, inicieu sessió a Roo Code Cloud o canvieu a un proveïdor diferent."
},
"mode_import_failed": "Ha fallat la importació del mode: {{error}}"
},
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "La teva organització requereix autenticació."
},
"buttons": {
"signIn": "Inicia sessió",
"settings": "Configuració"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras-Vervollständigungsfehler: {{error}}"
},
"roo": {
"authenticationRequired": "Roo-Anbieter erfordert Cloud-Authentifizierung. Bitte melde dich bei Roo Code Cloud an."
"authenticationRequired": "Roo-Anbieter erfordert Cloud-Authentifizierung. Bitte melde dich bei Roo Code Cloud an.",
"authenticationRequiredWithOptions": "Roo-Anbieter erfordert Authentifizierung. Bitte melde dich bei Roo Code Cloud an oder wechsle zu einem anderen Anbieter."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Deine Organisation erfordert eine Authentifizierung."
},
"buttons": {
"signIn": "Anmelden",
"settings": "Einstellungen"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras completion error: {{error}}"
},
"roo": {
"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud."
"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud.",
"authenticationRequiredWithOptions": "Roo provider requires authentication. Please sign in to Roo Code Cloud or switch to a different provider."
}
},
"warnings": {
@ -183,6 +184,11 @@
},
"info": {
"organization_requires_auth": "Your organization requires authentication."
},
"buttons": {
"signIn": "Sign In",
"settings": "Settings"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Error de finalización de Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "El proveedor Roo requiere autenticación en la nube. Por favor, inicia sesión en Roo Code Cloud."
"authenticationRequired": "El proveedor Roo requiere autenticación en la nube. Por favor, inicia sesión en Roo Code Cloud.",
"authenticationRequiredWithOptions": "El proveedor Roo requiere autenticación. Por favor, inicia sesión en Roo Code Cloud o cambia a un proveedor diferente."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Tu organización requiere autenticación."
},
"buttons": {
"signIn": "Iniciar sesión",
"settings": "Configuración"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Erreur d'achèvement de Cerebras : {{error}}"
},
"roo": {
"authenticationRequired": "Le fournisseur Roo nécessite une authentification cloud. Veuillez vous connecter à Roo Code Cloud."
"authenticationRequired": "Le fournisseur Roo nécessite une authentification cloud. Veuillez vous connecter à Roo Code Cloud.",
"authenticationRequiredWithOptions": "Le fournisseur Roo nécessite une authentification. Veuillez vous connecter à Roo Code Cloud ou passer à un autre fournisseur."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Votre organisation nécessite une authentification."
},
"buttons": {
"signIn": "Se connecter",
"settings": "Paramètres"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras पूर्णता त्रुटि: {{error}}"
},
"roo": {
"authenticationRequired": "Roo प्रदाता को क्लाउड प्रमाणीकरण की आवश्यकता है। कृपया Roo Code Cloud में साइन इन करें।"
"authenticationRequired": "Roo प्रदाता को क्लाउड प्रमाणीकरण की आवश्यकता है। कृपया Roo Code Cloud में साइन इन करें।",
"authenticationRequiredWithOptions": "Roo प्रदाता को प्रमाणीकरण की आवश्यकता है। कृपया Roo Code Cloud में साइन इन करें या किसी अन्य प्रदाता पर स्विच करें।"
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "आपके संगठन को प्रमाणीकरण की आवश्यकता है।"
},
"buttons": {
"signIn": "साइन इन करें",
"settings": "सेटिंग्स"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Kesalahan penyelesaian Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "Penyedia Roo memerlukan autentikasi cloud. Silakan masuk ke Roo Code Cloud."
"authenticationRequired": "Penyedia Roo memerlukan autentikasi cloud. Silakan masuk ke Roo Code Cloud.",
"authenticationRequiredWithOptions": "Penyedia Roo memerlukan autentikasi. Silakan masuk ke Roo Code Cloud atau beralih ke penyedia yang berbeda."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Organisasi kamu memerlukan autentikasi."
},
"buttons": {
"signIn": "Masuk",
"settings": "Pengaturan"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Errore di completamento Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "Il provider Roo richiede l'autenticazione cloud. Accedi a Roo Code Cloud."
"authenticationRequired": "Il provider Roo richiede l'autenticazione cloud. Accedi a Roo Code Cloud.",
"authenticationRequiredWithOptions": "Il provider Roo richiede l'autenticazione. Accedi a Roo Code Cloud o passa a un provider diverso."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "La tua organizzazione richiede l'autenticazione."
},
"buttons": {
"signIn": "Accedi",
"settings": "Impostazioni"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras完了エラー: {{error}}"
},
"roo": {
"authenticationRequired": "Rooプロバイダーはクラウド認証が必要です。Roo Code Cloudにサインインしてください。"
"authenticationRequired": "Rooプロバイダーはクラウド認証が必要です。Roo Code Cloudにサインインしてください。",
"authenticationRequiredWithOptions": "Rooプロバイダーは認証が必要です。Roo Code Cloudにサインインするか、別のプロバイダーに切り替えてください。"
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "あなたの組織では認証が必要です。"
},
"buttons": {
"signIn": "サインイン",
"settings": "設定"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras 완료 오류: {{error}}"
},
"roo": {
"authenticationRequired": "Roo 제공업체는 클라우드 인증이 필요합니다. Roo Code Cloud에 로그인하세요."
"authenticationRequired": "Roo 제공업체는 클라우드 인증이 필요합니다. Roo Code Cloud에 로그인하세요.",
"authenticationRequiredWithOptions": "Roo 제공업체는 인증이 필요합니다. Roo Code Cloud에 로그인하거나 다른 제공업체로 변경하세요."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "조직에서 인증이 필요합니다."
},
"buttons": {
"signIn": "로그인",
"settings": "설정"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras-voltooiingsfout: {{error}}"
},
"roo": {
"authenticationRequired": "Roo provider vereist cloud authenticatie. Log in bij Roo Code Cloud."
"authenticationRequired": "Roo provider vereist cloud authenticatie. Log in bij Roo Code Cloud.",
"authenticationRequiredWithOptions": "Roo provider vereist authenticatie. Log in bij Roo Code Cloud of schakel over naar een andere provider."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Je organisatie vereist authenticatie."
},
"buttons": {
"signIn": "Inloggen",
"settings": "Instellingen"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Błąd uzupełniania Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "Dostawca Roo wymaga uwierzytelnienia w chmurze. Zaloguj się do Roo Code Cloud."
"authenticationRequired": "Dostawca Roo wymaga uwierzytelnienia w chmurze. Zaloguj się do Roo Code Cloud.",
"authenticationRequiredWithOptions": "Dostawca Roo wymaga uwierzytelnienia. Zaloguj się do Roo Code Cloud lub przełącz na innego dostawcę."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Twoja organizacja wymaga uwierzytelnienia."
},
"buttons": {
"signIn": "Zaloguj się",
"settings": "Ustawienia"
}
}
},
"prompts": {

View file

@ -106,7 +106,8 @@
"completionError": "Erro de conclusão do Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "O provedor Roo requer autenticação na nuvem. Faça login no Roo Code Cloud."
"authenticationRequired": "O provedor Roo requer autenticação na nuvem. Faça login no Roo Code Cloud.",
"authenticationRequiredWithOptions": "O provedor Roo requer autenticação. Faça login no Roo Code Cloud ou mude para um provedor diferente."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Sua organização requer autenticação."
},
"buttons": {
"signIn": "Entrar",
"settings": "Configurações"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Ошибка завершения Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "Провайдер Roo требует облачной аутентификации. Войдите в Roo Code Cloud."
"authenticationRequired": "Провайдер Roo требует облачной аутентификации. Войдите в Roo Code Cloud.",
"authenticationRequiredWithOptions": "Провайдер Roo требует аутентификации. Войдите в Roo Code Cloud или переключитесь на другого провайдера."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Ваша организация требует аутентификации."
},
"buttons": {
"signIn": "Войти",
"settings": "Настройки"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Cerebras tamamlama hatası: {{error}}"
},
"roo": {
"authenticationRequired": "Roo sağlayıcısı bulut kimlik doğrulaması gerektirir. Lütfen Roo Code Cloud'a giriş yapın."
"authenticationRequired": "Roo sağlayıcısı bulut kimlik doğrulaması gerektirir. Lütfen Roo Code Cloud'a giriş yapın.",
"authenticationRequiredWithOptions": "Roo sağlayıcısı kimlik doğrulaması gerektirir. Lütfen Roo Code Cloud'a giriş yapın veya farklı bir sağlayıcıya geçin."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Kuruluşunuz kimlik doğrulaması gerektiriyor."
},
"buttons": {
"signIn": "Oturum Aç",
"settings": "Ayarlar"
}
}
},
"prompts": {

View file

@ -102,7 +102,8 @@
"completionError": "Lỗi hoàn thành Cerebras: {{error}}"
},
"roo": {
"authenticationRequired": "Nhà cung cấp Roo yêu cầu xác thực đám mây. Vui lòng đăng nhập vào Roo Code Cloud."
"authenticationRequired": "Nhà cung cấp Roo yêu cầu xác thực đám mây. Vui lòng đăng nhập vào Roo Code Cloud.",
"authenticationRequiredWithOptions": "Nhà cung cấp Roo yêu cầu xác thực. Vui lòng đăng nhập vào Roo Code Cloud hoặc chuyển sang nhà cung cấp khác."
}
},
"warnings": {
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "Tổ chức của bạn yêu cầu xác thực."
},
"buttons": {
"signIn": "Đăng nhập",
"settings": "Cài đặt"
}
}
},
"prompts": {

View file

@ -107,7 +107,8 @@
"completionError": "Cerebras 完成错误:{{error}}"
},
"roo": {
"authenticationRequired": "Roo 提供商需要云认证。请登录 Roo Code Cloud。"
"authenticationRequired": "Roo 提供商需要云认证。请登录 Roo Code Cloud。",
"authenticationRequiredWithOptions": "Roo 提供商需要身份验证。请登录 Roo Code Cloud 或切换到其他提供商。"
}
},
"warnings": {
@ -199,6 +200,11 @@
},
"info": {
"organization_requires_auth": "您的组织需要身份验证。"
},
"buttons": {
"signIn": "登录",
"settings": "设置"
}
}
},
"prompts": {

View file

@ -101,7 +101,8 @@
"completionError": "Cerebras 完成錯誤:{{error}}"
},
"roo": {
"authenticationRequired": "Roo 提供者需要雲端認證。請登入 Roo Code Cloud。"
"authenticationRequired": "Roo 提供者需要雲端認證。請登入 Roo Code Cloud。",
"authenticationRequiredWithOptions": "Roo 提供者需要身分驗證。請登入 Roo Code Cloud 或切換至其他提供者。"
},
"mode_import_failed": "匯入模式失敗:{{error}}"
},
@ -194,6 +195,11 @@
},
"info": {
"organization_requires_auth": "您的組織需要身份驗證。"
},
"buttons": {
"signIn": "登入",
"settings": "設定"
}
}
},
"prompts": {

View file

@ -119,8 +119,10 @@ export interface ExtensionMessage {
| "showEditMessageDialog"
| "commands"
| "insertTextIntoTextarea"
| "taskCreationFailed"
text?: string
payload?: any // Add a generic payload for now, can refine later
reason?: string // Reason for task creation failure
action?:
| "chatButtonClicked"
| "mcpButtonClicked"

View file

@ -875,6 +875,17 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
setIsCondensing(false)
}
break
case "taskCreationFailed":
// Re-enable the UI when task creation fails (e.g., Roo authentication error)
if (message.reason === "rooAuthenticationError") {
setSendingDisabled(false)
setClineAsk(undefined)
setEnableButtons(false)
// Clear any other UI state that might be stuck
setPrimaryButtonText(undefined)
setSecondaryButtonText(undefined)
}
break
}
// textAreaRef.current is not explicitly required here since React
// guarantees that ref will be stable across re-renders, and we're