Skip to content

Commit 64c79e6

Browse files
daniel-lxsroomote
authored andcommitted
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.
1 parent 4216618 commit 64c79e6

File tree

23 files changed

+227
-21
lines changed

23 files changed

+227
-21
lines changed

src/api/index.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import type { ProviderSettings, ModelInfo } from "@roo-code/types"
44

55
import { ApiStream } from "./transform/stream"
66

7+
// Custom error class for Roo authentication failures
8+
export class RooAuthenticationError extends Error {
9+
constructor(message: string) {
10+
super(message)
11+
this.name = "RooAuthenticationError"
12+
}
13+
}
14+
715
import {
816
GlamaHandler,
917
AnthropicHandler,
@@ -143,7 +151,15 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
143151
case "io-intelligence":
144152
return new IOIntelligenceHandler(options)
145153
case "roo":
146-
return new RooHandler(options)
154+
try {
155+
return new RooHandler(options)
156+
} catch (error) {
157+
// If Roo authentication fails, throw a specific error that can be caught upstream
158+
// This allows proper handling in the UI rather than silently falling back
159+
const errorMessage = error instanceof Error ? error.message : "Authentication required for Roo provider"
160+
console.error("[API] Roo provider authentication failed:", errorMessage)
161+
throw new RooAuthenticationError(errorMessage)
162+
}
147163
case "featherless":
148164
return new FeatherlessHandler(options)
149165
default:

src/core/webview/ClineProvider.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ import { setPanel } from "../../activate/registerCommands"
7575

7676
import { t } from "../../i18n"
7777

78-
import { buildApiHandler } from "../../api"
78+
import { buildApiHandler, RooAuthenticationError } from "../../api"
7979
import { forceFullModelDetailsLoad, hasLoadedFullDetails } from "../../api/providers/fetchers/lmstudio"
8080

8181
import { ContextProxy } from "../config/ContextProxy"
@@ -776,6 +776,44 @@ export class ClineProvider
776776
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
777777
}
778778

779+
// Check if Roo provider is selected and not authenticated BEFORE creating the task
780+
if (apiConfiguration.apiProvider === "roo") {
781+
try {
782+
// Try to build the API handler to check if authentication works
783+
buildApiHandler(apiConfiguration)
784+
} catch (error) {
785+
if (error instanceof RooAuthenticationError) {
786+
// Show error message to user
787+
const signInText = t("common:mdm.buttons.signIn")
788+
const settingsText = t("common:mdm.buttons.settings")
789+
const selection = await vscode.window.showErrorMessage(
790+
t("common:errors.roo.authenticationRequiredWithOptions"),
791+
signInText,
792+
settingsText,
793+
)
794+
795+
if (selection === signInText) {
796+
// Navigate to account view
797+
await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" })
798+
} else if (selection === settingsText) {
799+
// Open settings to allow provider change
800+
await this.postMessageToWebview({ type: "action", action: "switchTab", tab: "settings" })
801+
}
802+
803+
// Return the current task if it exists to prevent UI issues
804+
const currentTask = this.getCurrentTask()
805+
if (currentTask) {
806+
return currentTask
807+
}
808+
809+
// Throw a specific error that the webview can handle
810+
throw new RooAuthenticationError("Authentication required for Roo provider")
811+
}
812+
// Re-throw other errors
813+
throw error
814+
}
815+
}
816+
779817
const task = new Task({
780818
provider: this,
781819
apiConfiguration,

src/core/webview/webviewMessageHandler.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,38 @@ export const webviewMessageHandler = async (
288288
// Initializing new instance of Cline will make sure that any
289289
// agentically running promises in old instance don't affect our new
290290
// task. This essentially creates a fresh slate for the new task.
291-
await provider.createTask(message.text, message.images)
291+
try {
292+
await provider.createTask(message.text, message.images)
293+
// Task created successfully - notify the UI to reset
294+
await provider.postMessageToWebview({
295+
type: "invoke",
296+
invoke: "newChat",
297+
})
298+
} catch (error) {
299+
// Check if it's a RooAuthenticationError by checking the error name
300+
// The RooAuthenticationError class sets error.name = 'RooAuthenticationError'
301+
if (error instanceof Error && error.name === "RooAuthenticationError") {
302+
// The error has already been handled in createTask with a user-friendly dialog
303+
// Send a specific message to unlock the UI without resetting the chat
304+
provider.log(`Task creation cancelled due to Roo authentication: ${error.message}`)
305+
await provider.postMessageToWebview({
306+
type: "taskCreationFailed",
307+
reason: "rooAuthenticationError",
308+
})
309+
// Exit without sending newChat to keep the chat state
310+
break
311+
}
312+
313+
// For all other errors, reset the UI and show error
314+
await provider.postMessageToWebview({
315+
type: "invoke",
316+
invoke: "newChat",
317+
})
318+
// Show error to user
319+
vscode.window.showErrorMessage(
320+
`Failed to create task: ${error instanceof Error ? error.message : String(error)}`,
321+
)
322+
}
292323
break
293324
case "customInstructions":
294325
await provider.updateCustomInstructions(message.text)

src/i18n/locales/ca/common.json

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

src/i18n/locales/de/common.json

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

src/i18n/locales/en/common.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@
102102
"completionError": "Cerebras completion error: {{error}}"
103103
},
104104
"roo": {
105-
"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud."
105+
"authenticationRequired": "Roo provider requires cloud authentication. Please sign in to Roo Code Cloud.",
106+
"authenticationRequiredWithOptions": "Roo provider requires authentication. Please sign in to Roo Code Cloud or switch to a different provider."
106107
}
107108
},
108109
"warnings": {
@@ -183,6 +184,11 @@
183184
},
184185
"info": {
185186
"organization_requires_auth": "Your organization requires authentication."
187+
},
188+
"buttons": {
189+
"signIn": "Sign In",
190+
"settings": "Settings"
191+
}
186192
}
187193
},
188194
"prompts": {

src/i18n/locales/es/common.json

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

src/i18n/locales/fr/common.json

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

src/i18n/locales/hi/common.json

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

src/i18n/locales/id/common.json

Lines changed: 7 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)