Skip to content
Closed
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
18 changes: 17 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhancement: Consider adding JSDoc documentation for better code maintainability:

/**
 * Custom error class for Roo provider authentication failures.
 * Thrown when the Roo provider is selected but user is not authenticated.
 */
export class RooAuthenticationError extends Error {
    constructor(message: string) {
        super(message)
        this.name = "RooAuthenticationError"
        // Ensure proper prototype chain for instanceof checks
        Object.setPrototypeOf(this, RooAuthenticationError.prototype)
    }
}

}
}

import {
GlamaHandler,
AnthropicHandler,
Expand Down Expand Up @@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Consider using the i18n system for this fallback error message to maintain consistency with other user-facing messages:

const errorMessage = error instanceof Error ? error.message : t('common:errors.roo.authenticationRequired')

console.error("[API] Roo provider authentication failed:", errorMessage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Since you're throwing the error on line 161, the console.error might be redundant. Consider either:

  1. Removing the console.error, or
  2. Using a more structured logging approach if debugging info is needed

throw new RooAuthenticationError(errorMessage)
}
case "featherless":
return new FeatherlessHandler(options)
default:
Expand Down
40 changes: 39 additions & 1 deletion src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Enhancement: Consider implementing a retry mechanism that could store the pending task and automatically retry after successful authentication:

if (selection === signInText) {
    // Store pending task for retry
    this.pendingTaskAfterAuth = { text, images, parentTask, options }
    await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" })
}

This would improve UX by not requiring users to manually restart their task.

}
// Re-throw other errors
throw error
}
}

const task = new Task({
provider: this,
apiConfiguration,
Expand Down
33 changes: 32 additions & 1 deletion src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type Safety: Consider using instanceof for better type safety:

import { RooAuthenticationError } from "../../api"

// Then use:
if (error instanceof RooAuthenticationError) {
    // Handle authentication error
}

This provides better type checking and is more maintainable than string comparison.

// 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)
Expand Down
7 changes: 6 additions & 1 deletion src/i18n/locales/ca/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/de/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -183,6 +184,10 @@
},
"info": {
"organization_requires_auth": "Your organization requires authentication."
},
"buttons": {
"signIn": "Sign In",
"settings": "Settings"
}
},
"prompts": {
Expand Down
7 changes: 6 additions & 1 deletion src/i18n/locales/es/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/fr/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/hi/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/id/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/it/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/ja/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/ko/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/nl/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/pl/common.json

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

7 changes: 6 additions & 1 deletion src/i18n/locales/pt-BR/common.json

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

Loading
Loading