You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] LanceDbIndex interpolates paths into LanceDB SQL predicates — apostrophes in file/workspace paths break index deletes (stale chunks stay retrievable) and retrieval #13233
LanceDbIndex builds its LanceDB SQL predicates by string interpolation of on-disk paths and workspace directories, and a single quote is a legal character in both:
awaitlanceTable.delete(`cachekey = '${cacheKey}' AND path = '${path}'`,);
retrieval path (_retrieveForTag):
query=query.where(`path LIKE '${directory}%'`).limit(300);
A path or workspace directory containing an apostrophe (don't.py, it's a test/…) produces an unparseable predicate and LanceDB throws Unterminated string literal. Consequences:
Deleted/renamed files stay retrievable.compute/insert paths use parameterized sqlite and Arrow row adds (no interpolation), so a file with a quote in its name indexes fine. When the file is later deleted or renamed, update() reaches the toDel loop (removeTag + del) and the interpolated delete throws. CodebaseIndexer catches per batch, logs a warning, and moves on — markComplete never runs for that batch, so the tag-catalog rows survive and every subsequent sync retries the same throw. The stale chunks remain in that tag's LanceDB table and continue to surface in @Codebase retrieval even though the source file is gone.
Batch aborts. Every other file sharing the failing file's filesPerBatch batch also never completes and is re-processed on every sync.
Workspace directories with an apostrophe break retrieval._retrieveForTag interpolates the directory into path LIKE '…%', so retrieval throws for the whole workspace.
This was found in a correctness study of derived-representation lifecycle handling (what happens to index entries when their source is revoked).
normal-path delete and the ''-escaped delete both succeed, isolating the interpolation as the cause.
Expected Behavior
Filesystem paths are arbitrary byte strings (modulo / and NUL); predicates built from them must be escaped (e.g. double the single quotes: 'don''t.ts' — verified to work against vectordb@0.4.20), or the filters should use a parameterized API. All interpolated sites in LanceDbIndex.ts (update() delete loop, _retrieveForTagpath LIKE) need it; other artifacts (FullTextSearchCodebaseIndex, sqlite) already use bound parameters and are unaffected.
Happy to provide the full standalone script (including the retrieval-side case) or a patch.
This cleanly isolates the index staleness bug. Beyond string-escaping, physical row deletes on vector tables frequently leave ghost entries during partial batch crashes.
A drop-in escaping fix for core/indexing/LanceDbIndex.ts:
constescapedPath=path.replace(/'/g,"''");awaitlanceTable.delete(`cachekey = '${cacheKey}' AND path = '${escapedPath}'`);
And similarly for _retrieveForTag:
constescapedDirectory=directory.replace(/'/g,"''");query=query.where(`path LIKE '${escapedDirectory}%'`).limit(300);
Would a targeted PR against LanceDbIndex.ts with your reproduction test case help unblock the sync?
Activity
RemanenetSpy commentedon Oct 16, 2026
This cleanly isolates the index staleness bug. Beyond string-escaping, physical row deletes on vector tables frequently leave ghost entries during partial batch crashes.
A drop-in escaping fix for
core/indexing/LanceDbIndex.ts:And similarly for
_retrieveForTag:Would a targeted PR against
LanceDbIndex.tswith your reproduction test case help unblock the sync?