fix(indexing): prevent LanceDB metadata type coercion causing full re-index on restart

LanceDB infers the value column type from the first row in _createMetadataData().
Since vector_size (1024) was stored as a number, all subsequent string values
(embedding_provider, embedding_model_id) and booleans (indexing_complete) were
silently coerced to NaN/0/1, corrupting metadata.

On restart, _getStoredEmbeddingProfile() received NaN values, causing
needsRecreation=true every time — the database was dropped and recreated,
the file-hash cache cleared, and a full re-index triggered on every VS Code reopen.

Store all metadata values as strings to ensure consistent column type.
This commit is contained in:
Bazhen Rzheutskii
2026-05-29 00:30:43 +04:00
parent cd69bca8bf
commit 13cbb35977
@@ -136,7 +136,7 @@ export class LanceDBVectorStore implements IVectorStore {
return [
{
key: KEY.size,
value: this.vectorSize,
value: String(this.vectorSize),
},
{
key: KEY.provider,
@@ -148,11 +148,11 @@ export class LanceDBVectorStore implements IVectorStore {
},
{
key: KEY.dimension,
value: this.profile.dimension,
value: String(this.profile.dimension),
},
{
key: KEY.complete,
value: false,
value: "false",
},
]
}
@@ -572,7 +572,7 @@ export class LanceDBVectorStore implements IVectorStore {
}
const metadataTable = await db.openTable(this.metadataTableName)
const metadataResults = await metadataTable.query().where(`key = '${KEY.complete}'`).toArray()
const indexed = metadataResults.length > 0 ? metadataResults[0].value : false
const indexed = metadataResults.length > 0 ? String(metadataResults[0].value) === "true" : false
log.info("LanceDB indexing metadata evaluated", {
workspacePath: this.workspacePath,
pointCount,
@@ -590,14 +590,16 @@ export class LanceDBVectorStore implements IVectorStore {
throw new Error(`Invalid metadata key: ${key}`)
}
await metadataTable.delete(`key = '${key}'`)
await metadataTable.add([{ key, value }])
// All values must be strings to prevent LanceDB from inferring the value column
// type as number from the first row, which corrupts subsequent string/boolean values.
await metadataTable.add([{ key, value: String(value) }])
}
private async _persistEmbeddingProfile(metadataTable: Table): Promise<void> {
await this._upsertMetadata(metadataTable, KEY.provider, this.profile.provider)
await this._upsertMetadata(metadataTable, KEY.model, this.profile.modelId)
await this._upsertMetadata(metadataTable, KEY.dimension, this.profile.dimension)
await this._upsertMetadata(metadataTable, KEY.size, this.vectorSize)
await this._upsertMetadata(metadataTable, KEY.dimension, String(this.profile.dimension))
await this._upsertMetadata(metadataTable, KEY.size, String(this.vectorSize))
}
/**
@@ -609,7 +611,7 @@ export class LanceDBVectorStore implements IVectorStore {
const db = await this.getDb()
const metadataTable = await db.openTable(this.metadataTableName)
await this._persistEmbeddingProfile(metadataTable)
await this._upsertMetadata(metadataTable, KEY.complete, true)
await this._upsertMetadata(metadataTable, KEY.complete, "true")
log.info("Marked indexing as complete")
} catch (error) {
log.error("Failed to mark indexing as complete", { error })
@@ -626,7 +628,7 @@ export class LanceDBVectorStore implements IVectorStore {
const db = await this.getDb()
const metadataTable = await db.openTable(this.metadataTableName)
await this._persistEmbeddingProfile(metadataTable)
await this._upsertMetadata(metadataTable, KEY.complete, false)
await this._upsertMetadata(metadataTable, KEY.complete, "false")
log.info("Marked indexing as incomplete (in progress)")
} catch (error) {
log.error("Failed to mark indexing as incomplete", { error })