Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/services/code-index/processors/file-watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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++
Expand Down
31 changes: 22 additions & 9 deletions src/services/code-index/processors/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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 },
)
}
Expand Down
61 changes: 48 additions & 13 deletions src/services/code-index/vector-store/qdrant-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down