abhigyanpatwari/GitNexus · error
[embed] Failed to delete stale embedding rows — aborting to…
Error message
[embed] Failed to delete stale embedding rows — aborting to prevent vector-index corruption: ${msg} What it means
Thrown by deleteStaleEmbeddingRows() during the incremental re-embed path: nodes whose contentHash changed must have their old Embedding rows DELETEd before re-embedding. Any DELETE failure other than a tolerated 'does not exist' (fresh index, table missing) aborts the pipeline, because leaving stale vectors would make the vector index return embeddings that no longer match node content — corruption by design is worse than stopping.
Solutions
- Stop other gitnexus processes using this repo's index (gitnexus serve, MCP server, another analyze) and rerun the embedding pipeline.
- Free disk space and verify write permissions on the .gitnexus directory.
- If the DB is corrupted, delete the .gitnexus index and re-run analyze --embeddings from scratch.
- Rerun the pipeline — the delete/re-embed is designed so a clean rerun converges.
Example fix
# before: serve is running while re-embedding npx gitnexus serve & npx gitnexus analyze --embeddings # after: single writer at a time kill %1 # stop serve first npx gitnexus analyze --embeddings npx gitnexus serve
Defensive patterns
Strategy: retry
Validate before calling
// ensure single writer before running the embedding pipeline
import { access, constants } from 'node:fs/promises';
await access('.gitnexus', constants.W_OK); // fail fast if index dir not writable Type guard
const isStaleDeleteAbort = (e: unknown): boolean =>
e instanceof Error && e.message.startsWith('[embed] Failed to delete stale embedding rows'); Try / catch
try {
await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, onProgress, config);
} catch (e) {
if (isStaleDeleteAbort(e)) {
// stop other gitnexus processes on this repo, then rerun the pipeline
} else throw e;
} Prevention
- Run only one gitnexus process (analyze/serve/MCP) against a repo index at a time.
- Monitor free disk space before long embedding runs.
- Treat this abort as intentional: never wrap it in a swallow-and-continue handler that would risk stale vectors.
When it happens
Trigger: Running the embedding pipeline in incremental mode (changed files only) while the LadybugDB file is locked by another process (e.g. a concurrent `gitnexus serve` or second analyze), on a disk I/O error, or against a corrupted .gitnexus database during the MATCH (e:Embedding {nodeId}) DELETE e batch.
Common situations: Two gitnexus processes sharing one repo index, an editor MCP server holding the DB while the CLI re-embeds, full disk, or a DB left dirty after a killed run.
Related errors
- deleteNodesForFile: table does not exist — skipping…
- deleteNodesForFiles: table does not exist — skipping…
- [embed] could not count persisted embeddings; leaving…
- Bridge query prepare failed
- GitNexus [query:vector]: vector index query failed; using…
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-08-20).
Data as JSON: /api/errors/deb9711751e33d24.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/embeddings/embedding-pipeline.ts:510
* interleaving bounds that window to a single batch.
*/
const deleteStaleEmbeddingRows = async (
executeWithReusedStatement: (
cypher: string,
paramsList: Array<Record<string, any>>,
) => Promise<void>,
nodeIds: string[],
): Promise<void> => {
if (nodeIds.length === 0) return;
try {
await executeWithReusedStatement(
`MATCH (e:${EMBEDDING_TABLE_NAME} {nodeId: $nodeId}) DELETE e`,
nodeIds.map((nodeId) => ({ nodeId })),
);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
if (!msg.includes('does not exist')) {
throw new Error(
`[embed] Failed to delete stale embedding rows — aborting to prevent vector-index corruption: ${msg}`,
);
}
}
};
/**
* Run the embedding pipeline
*
* @param executeQuery - Function to execute Cypher queries against LadybugDB
* @param executeWithReusedStatement - Function to execute with reused prepared statement
* @param onProgress - Callback for progress updates
* @param config - Optional configuration override
* @param skipNodeIds - Optional set of node IDs that already have embeddings (incremental mode)
* @param existingEmbeddings - Optional map of nodeId → contentHash for incremental mode.
* Nodes whose hash matches are skipped; nodes with a changed hash are DELETE'd
* and re-embedded; nodes not in the map are embedded fresh.
*/View on GitHub (pinned to ac9a4e9abd)