mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: prevent conversation history corruption when cancelling during response
- Add validation in abortTask to skip saving messages during active streaming - Add empty array checks in save methods to prevent corruption - Add validation in persistence layer to ensure valid message arrays - This fixes the race condition where cancelling during streaming could save empty arrays and wipe conversation history Fixes #8153
This commit is contained in:
parent
87b45def18
commit
7b22715557
3 changed files with 61 additions and 3 deletions
|
|
@ -77,6 +77,21 @@ export async function saveApiMessages({
|
|||
taskId: string
|
||||
globalStoragePath: string
|
||||
}) {
|
||||
// Validate messages array to prevent saving empty arrays that could corrupt history
|
||||
if (!messages || !Array.isArray(messages)) {
|
||||
console.error(
|
||||
`[Roo-Debug] saveApiMessages: Invalid messages provided for taskId: ${taskId}. Messages must be an array.`,
|
||||
)
|
||||
throw new Error("Invalid messages: must be an array")
|
||||
}
|
||||
|
||||
// Warn if saving empty array but allow it for new tasks
|
||||
if (messages.length === 0) {
|
||||
console.warn(
|
||||
`[Roo-Debug] saveApiMessages: Saving empty messages array for taskId: ${taskId}. This may be intentional for new tasks.`,
|
||||
)
|
||||
}
|
||||
|
||||
const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId)
|
||||
const filePath = path.join(taskDir, GlobalFileNames.apiConversationHistory)
|
||||
await safeWriteJson(filePath, messages)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,21 @@ export type SaveTaskMessagesOptions = {
|
|||
}
|
||||
|
||||
export async function saveTaskMessages({ messages, taskId, globalStoragePath }: SaveTaskMessagesOptions) {
|
||||
// Validate messages array to prevent saving empty arrays that could corrupt history
|
||||
if (!messages || !Array.isArray(messages)) {
|
||||
console.error(
|
||||
`[Roo-Debug] saveTaskMessages: Invalid messages provided for taskId: ${taskId}. Messages must be an array.`,
|
||||
)
|
||||
throw new Error("Invalid messages: must be an array")
|
||||
}
|
||||
|
||||
// Warn if saving empty array but allow it for new tasks
|
||||
if (messages.length === 0) {
|
||||
console.warn(
|
||||
`[Roo-Debug] saveTaskMessages: Saving empty messages array for taskId: ${taskId}. This may be intentional for new tasks.`,
|
||||
)
|
||||
}
|
||||
|
||||
const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId)
|
||||
const filePath = path.join(taskDir, GlobalFileNames.uiMessages)
|
||||
await safeWriteJson(filePath, messages)
|
||||
|
|
|
|||
|
|
@ -589,6 +589,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
|
||||
private async saveApiConversationHistory() {
|
||||
try {
|
||||
// Validate that we have messages before saving to prevent corruption
|
||||
if (!this.apiConversationHistory || this.apiConversationHistory.length === 0) {
|
||||
console.warn(
|
||||
`[Task#saveApiConversationHistory] Skipping save of empty API conversation history for task ${this.taskId}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
await saveApiMessages({
|
||||
messages: this.apiConversationHistory,
|
||||
taskId: this.taskId,
|
||||
|
|
@ -658,6 +666,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
|
||||
private async saveClineMessages() {
|
||||
try {
|
||||
// Validate that we have messages before saving to prevent corruption
|
||||
if (!this.clineMessages || this.clineMessages.length === 0) {
|
||||
console.warn(`[Task#saveClineMessages] Skipping save of empty Cline messages for task ${this.taskId}`)
|
||||
return
|
||||
}
|
||||
|
||||
await saveTaskMessages({
|
||||
messages: this.clineMessages,
|
||||
taskId: this.taskId,
|
||||
|
|
@ -1504,10 +1518,24 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
console.error(`Error during task ${this.taskId}.${this.instanceId} disposal:`, error)
|
||||
// Don't rethrow - we want abort to always succeed
|
||||
}
|
||||
// Save the countdown message in the automatic retry or other content.
|
||||
|
||||
// Only save messages if they are not empty to prevent corruption
|
||||
// This prevents the race condition where cancelling during streaming
|
||||
// could save an empty array and wipe the conversation history
|
||||
try {
|
||||
// Save the countdown message in the automatic retry or other content.
|
||||
await this.saveClineMessages()
|
||||
// Check if we have valid messages before saving
|
||||
// Don't save if messages are empty or if we're in the middle of streaming
|
||||
if (this.clineMessages && this.clineMessages.length > 0 && !this.isStreaming) {
|
||||
await this.saveClineMessages()
|
||||
} else if (this.isStreaming) {
|
||||
console.log(
|
||||
`[Task#abortTask] Skipping message save during active streaming for task ${this.taskId}.${this.instanceId}`,
|
||||
)
|
||||
} else {
|
||||
console.log(
|
||||
`[Task#abortTask] Skipping message save due to empty messages for task ${this.taskId}.${this.instanceId}`,
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error saving messages during abort for task ${this.taskId}.${this.instanceId}:`, error)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue