fix: handle non-existent workspace repo in checkpoint cleanup

- Check if workspace repo directory exists before git operations in deleteTask()
- Reduce log verbosity - only log actual errors, not expected cases
- Add test case for non-existent directory handling
This commit is contained in:
daniel-lxs 2026-01-19 23:50:35 -05:00 • committed by Hannes Rudolph
parent 523abdb25d
commit 7c0ce3ef66
3 changed files with 36 additions and 13 deletions

View file

@ -1802,8 +1802,8 @@ export class ClineProvider
try {
await ShadowCheckpointService.deleteTask({ taskId, globalStorageDir, workspaceDir })
} catch (error) {
console.error(
`[deleteTaskWithId${taskId}] failed to delete associated shadow repository or branch: ${error instanceof Error ? error.message : String(error)}`,
this.log(
`[deleteTaskWithId] failed to delete shadow repository for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`,
)
}
@ -1811,10 +1811,9 @@ export class ClineProvider
try {
const dirPath = await getTaskDirectoryPath(globalStoragePath, taskId)
await fs.rm(dirPath, { recursive: true, force: true })
console.log(`[deleteTaskWithId${taskId}] removed task directory`)
} catch (error) {
console.error(
`[deleteTaskWithId${taskId}] failed to remove task directory: ${error instanceof Error ? error.message : String(error)}`,
this.log(
`[deleteTaskWithId] failed to remove task directory for task ${taskId}: ${error instanceof Error ? error.message : String(error)}`,
)
}
}

View file

@ -449,22 +449,21 @@ export abstract class ShadowCheckpointService extends EventEmitter {
workspaceDir: string
}) {
const workspaceRepoDir = this.workspaceRepoDir({ globalStorageDir, workspaceDir })
// Check if the workspace repo directory exists before attempting git operations
if (!(await fileExistsAtPath(workspaceRepoDir))) {
return
}
const branchName = `roo-${taskId}`
const git = createSanitizedGit(workspaceRepoDir)
const success = await this.deleteBranch(git, branchName)
if (success) {
console.log(`[${this.name}#deleteTask.${taskId}] deleted branch ${branchName}`)
} else {
console.error(`[${this.name}#deleteTask.${taskId}] failed to delete branch ${branchName}`)
}
await this.deleteBranch(git, branchName)
}
public static async deleteBranch(git: SimpleGit, branchName: string) {
const branches = await git.branchLocal()
if (!branches.all.includes(branchName)) {
console.error(`[${this.constructor.name}#deleteBranch] branch ${branchName} does not exist`)
return false
}

View file

@ -928,5 +928,30 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])(
}
})
})
describe(`${klass.name}#deleteTask`, () => {
it("handles non-existent workspace repo directory gracefully", async () => {
const nonExistentWorkspaceDir = path.join(tmpDir, `non-existent-workspace-${Date.now()}`)
const nonExistentGlobalStorageDir = path.join(tmpDir, `non-existent-storage-${Date.now()}`)
const taskIdToDelete = "non-existent-task"
// Verify the workspace repo directory doesn't exist
const workspaceRepoDir = path.join(
nonExistentGlobalStorageDir,
"checkpoints",
klass.hashWorkspaceDir(nonExistentWorkspaceDir),
)
expect(await fileExistsAtPath(workspaceRepoDir)).toBe(false)
// Should not throw when the directory doesn't exist
await expect(
klass.deleteTask({
taskId: taskIdToDelete,
globalStorageDir: nonExistentGlobalStorageDir,
workspaceDir: nonExistentWorkspaceDir,
}),
).resolves.not.toThrow()
})
})
},
)