From ce0478fd55bd1f2b0da555806844a4ebe38e160d Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sun, 12 Apr 2026 03:18:41 +0000 Subject: [PATCH] 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 --- src/core/webview/ClineProvider.ts | 14 ++++- .../ClineProvider.sticky-mode.spec.ts | 58 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 2ffe421c095..d71edb98ef4 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -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. } diff --git a/src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts b/src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts index abef31af89f..4e08bfc749b 100644 --- a/src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts @@ -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", () => {