fix: prevent task deletion when files temporarily unavailable#11173
fix: prevent task deletion when files temporarily unavailable#11173roomote[bot] wants to merge 1 commit intomainfrom
Conversation
This commit addresses issue #11172 where parent tasks in delegation chains were being permanently deleted from task history when their api_conversation_history.json file was temporarily unavailable. Changes: - Remove destructive deleteTaskFromState() call from getTaskWithId() - Add TaskFileMissingError and TaskNotFoundError classes for proper error handling - Add retry logic with exponential backoff (3 retries, 100-400ms delays) - Update showTaskWithId() to show user-friendly error messages - Update deleteTaskWithId() to handle missing files gracefully - Update reopenParentFromDelegation() to recover from missing files - Add hasDelegationMetadata flag to identify tasks in delegation chains - Add comprehensive test coverage for new error handling behavior The key fix is that tasks with missing files are no longer deleted from history, preserving delegation chain integrity and preventing orphaned tasks.
Reviewed the changes. The implementation correctly addresses the issue of tasks being deleted when files are temporarily unavailable. Found one test quality issue:
Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues. |
| it("includes hasDelegationMetadata flag when task has parentTaskId", async () => { | ||
| await provider.resolveWebviewView(mockWebviewView) | ||
| provider.isViewLaunched = true | ||
|
|
||
| const historyItem = createHistoryItem({ | ||
| id: "child-task", | ||
| task: "Child task", | ||
| parentTaskId: "parent-task", | ||
| }) | ||
|
|
||
| // Set up task history with this item | ||
| ;(mockContext.globalState.get as ReturnType<typeof vi.fn>).mockImplementation((key: string) => { | ||
| if (key === "taskHistory") return [historyItem] | ||
| return undefined | ||
| }) | ||
|
|
||
| try { | ||
| await provider.getTaskWithId("child-task") | ||
| } catch (error) { | ||
| expect(error).toBeInstanceOf(TaskFileMissingError) | ||
| expect((error as TaskFileMissingError).hasDelegationMetadata).toBe(true) | ||
| } | ||
| }) |
There was a problem hiding this comment.
These tests use try-catch with assertions inside the catch block. If getTaskWithId doesn't throw (e.g., due to future mocking changes), the assertions won't run and the test will pass silently. Consider using expect.assertions(2) at the start of each test or refactoring to use await expect(...).rejects.toThrow(...) followed by a separate assertion for hasDelegationMetadata.
| it("includes hasDelegationMetadata flag when task has parentTaskId", async () => { | |
| await provider.resolveWebviewView(mockWebviewView) | |
| provider.isViewLaunched = true | |
| const historyItem = createHistoryItem({ | |
| id: "child-task", | |
| task: "Child task", | |
| parentTaskId: "parent-task", | |
| }) | |
| // Set up task history with this item | |
| ;(mockContext.globalState.get as ReturnType<typeof vi.fn>).mockImplementation((key: string) => { | |
| if (key === "taskHistory") return [historyItem] | |
| return undefined | |
| }) | |
| try { | |
| await provider.getTaskWithId("child-task") | |
| } catch (error) { | |
| expect(error).toBeInstanceOf(TaskFileMissingError) | |
| expect((error as TaskFileMissingError).hasDelegationMetadata).toBe(true) | |
| } | |
| }) | |
| it("includes hasDelegationMetadata flag when task has parentTaskId", async () => { | |
| await provider.resolveWebviewView(mockWebviewView) | |
| provider.isViewLaunched = true | |
| const historyItem = createHistoryItem({ | |
| id: "child-task", | |
| task: "Child task", | |
| parentTaskId: "parent-task", | |
| }) | |
| // Set up task history with this item | |
| ;(mockContext.globalState.get as ReturnType<typeof vi.fn>).mockImplementation((key: string) => { | |
| if (key === "taskHistory") return [historyItem] | |
| return undefined | |
| }) | |
| const error = await provider.getTaskWithId("child-task").catch((e) => e) | |
| expect(error).toBeInstanceOf(TaskFileMissingError) | |
| expect((error as TaskFileMissingError).hasDelegationMetadata).toBe(true) | |
| }) |
Fix it with Roo Code or mention @roomote and request a fix.
Summary
This PR attempts to address Issue #11172 where parent tasks in delegation chains were being permanently deleted from task history when their
api_conversation_history.jsonfile was temporarily unavailable.Problem
When
getTaskWithId()was called and the task file was missing (due to disk I/O latency or race conditions during delegation transitions), the code unconditionally deleted the task from state, which:Solution
deleteTaskFromState()call fromgetTaskWithId()- the primary fixTaskFileMissingErrorandTaskNotFoundErrorerror classes for proper error handlingshowTaskWithId()to show user-friendly error messages instead of deleting tasksdeleteTaskWithId()to handle missing files gracefully using history metadatareopenParentFromDelegation()to recover from missing files by falling back to history metadatahasDelegationMetadataflag to identify tasks with delegation relationships (parentTaskId, childIds, etc.)Key Changes
src/utils/errors.ts: AddedTaskFileMissingErrorandTaskNotFoundErrorclassessrc/core/webview/ClineProvider.ts: ModifiedgetTaskWithId(),showTaskWithId(),deleteTaskWithId(), andreopenParentFromDelegation()src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts: Added comprehensive tests for new error handling behaviorTesting
TaskNotFoundErrorthrown when task does not existTaskFileMissingErrorthrown when file is missinghasDelegationMetadataflag properly set for delegation chainsvscode.window.showErrorMessageFeedback and guidance are welcome!
Important
Fixes task deletion issue by adding error handling and retry logic for missing files in task history.
deleteTaskFromState()fromgetTaskWithId()to prevent task deletion when files are missing.getTaskWithId()for file I/O issues.showTaskWithId()to display error messages instead of deleting tasks.deleteTaskWithId()to handle missing files using history metadata.reopenParentFromDelegation()to recover from missing files using history metadata.TaskFileMissingErrorandTaskNotFoundErrorinerrors.tsfor better error handling.ClineProvider.taskHistory.spec.tsfor new error handling and retry logic.This description was created by
for 349e2c5. You can customize this summary. It will automatically update as commits are pushed.