fix: handle activateProviderProfile errors gracefully during mode switch

When switching modes, if activateProviderProfile fails (e.g. due to
corrupted profile data or proxy provider configuration issues), the
error would propagate up and leave the mode in an inconsistent state -
the mode was already updated in memory and global state, but the API
configuration was not switched.

This wraps the activateProviderProfile call in handleModeSwitch with
a try-catch so the mode switch completes gracefully, falling back to
the current API configuration. The error is logged for diagnostics.

Closes #12098
This commit is contained in:
Roo Code 2026-04-12 03:18:41 +00:00
parent 7adbfec2a4
commit ce0478fd55
2 changed files with 71 additions and 1 deletions

View file

@ -1455,7 +1455,19 @@ export class ClineProvider
const hasActualSettings = !!fullProfile.apiProvider
if (hasActualSettings) {
await this.activateProviderProfile({ name: profile.name })
try {
await this.activateProviderProfile({ name: profile.name })
} catch (error) {
// If profile activation fails (e.g. corrupted profile data,
// proxy provider configuration issues), log the error but
// allow the mode switch to complete gracefully. The task will
// continue with the current API configuration.
this.log(
`Failed to activate provider profile "${profile.name}" during mode switch to "${newMode}": ${
error instanceof Error ? error.message : String(error)
}`,
)
}
} else {
// The task will continue with the current/default configuration.
}

View file

@ -1044,6 +1044,64 @@ describe("ClineProvider - Sticky Mode", () => {
consoleErrorSpy.mockRestore()
})
it("should complete mode switch gracefully when activateProviderProfile fails", async () => {
await provider.resolveWebviewView(mockWebviewView)
const mockTask = {
taskId: "test-task-id",
_taskMode: "code",
emit: vi.fn(),
saveClineMessages: vi.fn(),
clineMessages: [],
apiConversationHistory: [],
updateApiConfiguration: vi.fn(),
}
await provider.addClineToStack(mockTask as any)
// Mock getGlobalState to return task history
vi.spyOn(provider as any, "getGlobalState").mockReturnValue([
{
id: mockTask.taskId,
ts: Date.now(),
task: "Test task",
number: 1,
tokensIn: 0,
tokensOut: 0,
cacheWrites: 0,
cacheReads: 0,
totalCost: 0,
},
])
// Mock updateTaskHistory to succeed
vi.spyOn(provider, "updateTaskHistory").mockImplementation(() => Promise.resolve([]))
// Mock providerSettingsManager to return a saved config for the target mode
const mockConfigId = "test-config-id"
vi.spyOn(provider.providerSettingsManager, "getModeConfigId").mockResolvedValue(mockConfigId)
vi.spyOn(provider.providerSettingsManager, "listConfig").mockResolvedValue([
{ id: mockConfigId, name: "test-profile" },
])
vi.spyOn(provider.providerSettingsManager, "getProfile").mockResolvedValue({
id: mockConfigId,
name: "test-profile",
apiProvider: "openai" as any,
})
// Mock activateProviderProfile to throw (simulating a proxy provider configuration issue)
vi.spyOn(provider, "activateProviderProfile").mockRejectedValue(
new Error("Failed to activate profile: connection error"),
)
// The mode switch should complete without throwing
await expect(provider.handleModeSwitch("architect")).resolves.not.toThrow()
// The mode should still be updated despite profile activation failure
expect(mockTask._taskMode).toBe("architect")
expect(vi.mocked(mockContext.globalState.update)).toHaveBeenCalledWith("mode", "architect")
})
})
describe("Multiple tasks switching modes simultaneously", () => {