mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: handle Qdrant deletion errors gracefully to prevent indexing interruption (#6296)
This commit is contained in:
parent
1da82b2db0
commit
5041880da0
3 changed files with 78 additions and 25 deletions
|
|
@ -204,15 +204,20 @@ export class FileWatcher implements IFileWatcher {
|
|||
currentFile: path,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
overallBatchError = error as Error
|
||||
} catch (error: any) {
|
||||
const errorStatus = error?.status || error?.response?.status || error?.statusCode
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
|
||||
// Log telemetry for deletion error
|
||||
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
|
||||
error: sanitizeErrorMessage(overallBatchError.message),
|
||||
error: sanitizeErrorMessage(errorMessage),
|
||||
location: "deletePointsByMultipleFilePaths",
|
||||
errorType: "deletion_error",
|
||||
errorStatus: errorStatus,
|
||||
})
|
||||
|
||||
// Mark all paths as error
|
||||
overallBatchError = error as Error
|
||||
for (const path of pathsToExplicitlyDelete) {
|
||||
batchResults.push({ path, status: "error", error: error as Error })
|
||||
processedCountInBatch++
|
||||
|
|
|
|||
|
|
@ -281,17 +281,24 @@ export class DirectoryScanner implements IDirectoryScanner {
|
|||
try {
|
||||
await this.qdrantClient.deletePointsByFilePath(cachedFilePath)
|
||||
await this.cacheManager.deleteHash(cachedFilePath)
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
const errorStatus = error?.status || error?.response?.status || error?.statusCode
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
|
||||
console.error(
|
||||
`[DirectoryScanner] Failed to delete points for ${cachedFilePath} in workspace ${scanWorkspace}:`,
|
||||
error,
|
||||
)
|
||||
|
||||
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
|
||||
error: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
||||
error: sanitizeErrorMessage(errorMessage),
|
||||
stack: error instanceof Error ? sanitizeErrorMessage(error.stack || "") : undefined,
|
||||
location: "scanDirectory:deleteRemovedFiles",
|
||||
errorStatus: errorStatus,
|
||||
})
|
||||
|
||||
if (onError) {
|
||||
// Report error to error handler
|
||||
onError(
|
||||
error instanceof Error
|
||||
? new Error(
|
||||
|
|
@ -304,7 +311,8 @@ export class DirectoryScanner implements IDirectoryScanner {
|
|||
),
|
||||
)
|
||||
}
|
||||
// Decide if we should re-throw or just log
|
||||
// Log error and continue processing instead of re-throwing
|
||||
console.error(`Failed to delete points for removed file: ${cachedFilePath}`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -347,25 +355,30 @@ export class DirectoryScanner implements IDirectoryScanner {
|
|||
if (uniqueFilePaths.length > 0) {
|
||||
try {
|
||||
await this.qdrantClient.deletePointsByMultipleFilePaths(uniqueFilePaths)
|
||||
} catch (deleteError) {
|
||||
} catch (deleteError: any) {
|
||||
const errorStatus =
|
||||
deleteError?.status || deleteError?.response?.status || deleteError?.statusCode
|
||||
const errorMessage = deleteError instanceof Error ? deleteError.message : String(deleteError)
|
||||
|
||||
console.error(
|
||||
`[DirectoryScanner] Failed to delete points for ${uniqueFilePaths.length} files before upsert in workspace ${scanWorkspace}:`,
|
||||
deleteError,
|
||||
)
|
||||
|
||||
TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
|
||||
error: sanitizeErrorMessage(
|
||||
deleteError instanceof Error ? deleteError.message : String(deleteError),
|
||||
),
|
||||
error: sanitizeErrorMessage(errorMessage),
|
||||
stack:
|
||||
deleteError instanceof Error
|
||||
? sanitizeErrorMessage(deleteError.stack || "")
|
||||
: undefined,
|
||||
location: "processBatch:deletePointsByMultipleFilePaths",
|
||||
fileCount: uniqueFilePaths.length,
|
||||
errorStatus: errorStatus,
|
||||
})
|
||||
// Re-throw the error with workspace context
|
||||
|
||||
// Re-throw with workspace context
|
||||
throw new Error(
|
||||
`Failed to delete points for ${uniqueFilePaths.length} files. Workspace: ${scanWorkspace}. ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`,
|
||||
`Failed to delete points for ${uniqueFilePaths.length} files. Workspace: ${scanWorkspace}. ${errorMessage}`,
|
||||
{ cause: deleteError },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,27 +423,62 @@ export class QdrantVectorStore implements IVectorStore {
|
|||
}
|
||||
|
||||
try {
|
||||
// First check if the collection exists
|
||||
const collectionExists = await this.collectionExists()
|
||||
if (!collectionExists) {
|
||||
console.warn(
|
||||
`[QdrantVectorStore] Skipping deletion - collection "${this.collectionName}" does not exist`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const workspaceRoot = getWorkspacePath()
|
||||
const normalizedPaths = filePaths.map((filePath) => {
|
||||
const absolutePath = path.resolve(workspaceRoot, filePath)
|
||||
return path.normalize(absolutePath)
|
||||
|
||||
// Build filters using pathSegments to match the indexed fields
|
||||
const filters = filePaths.map((filePath) => {
|
||||
// IMPORTANT: Use the relative path to match what's stored in upsertPoints
|
||||
// upsertPoints stores the relative filePath, not the absolute path
|
||||
const relativePath = path.isAbsolute(filePath) ? path.relative(workspaceRoot, filePath) : filePath
|
||||
|
||||
// Normalize the relative path
|
||||
const normalizedRelativePath = path.normalize(relativePath)
|
||||
|
||||
// Split the path into segments like we do in upsertPoints
|
||||
const segments = normalizedRelativePath.split(path.sep).filter(Boolean)
|
||||
|
||||
// Create a filter that matches all segments of the path
|
||||
// This ensures we only delete points that match the exact file path
|
||||
const mustConditions = segments.map((segment, index) => ({
|
||||
key: `pathSegments.${index}`,
|
||||
match: { value: segment },
|
||||
}))
|
||||
|
||||
return { must: mustConditions }
|
||||
})
|
||||
|
||||
const filter = {
|
||||
should: normalizedPaths.map((normalizedPath) => ({
|
||||
key: "filePath",
|
||||
match: {
|
||||
value: normalizedPath,
|
||||
},
|
||||
})),
|
||||
}
|
||||
// Use 'should' to match any of the file paths (OR condition)
|
||||
const filter = filters.length === 1 ? filters[0] : { should: filters }
|
||||
|
||||
await this.client.delete(this.collectionName, {
|
||||
filter,
|
||||
wait: true,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Failed to delete points by file paths:", error)
|
||||
} catch (error: any) {
|
||||
// Extract more detailed error information
|
||||
const errorMessage = error?.message || String(error)
|
||||
const errorStatus = error?.status || error?.response?.status || error?.statusCode
|
||||
const errorDetails = error?.response?.data || error?.data || ""
|
||||
|
||||
console.error(`[QdrantVectorStore] Failed to delete points by file paths:`, {
|
||||
error: errorMessage,
|
||||
status: errorStatus,
|
||||
details: errorDetails,
|
||||
collection: this.collectionName,
|
||||
fileCount: filePaths.length,
|
||||
// Include first few file paths for debugging (avoid logging too many)
|
||||
samplePaths: filePaths.slice(0, 3),
|
||||
})
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue