abhigyanpatwari/GitNexus · warning
lockErr
Error message
lockErr
What it means
A 409 Conflict returned when acquireRepoLock fails during repo deletion. GitNexus holds a per-storage-path lock so a repo is not deleted while an analyze or embed job is reading/writing its index. The body's error field explains which operation holds the lock.
Solutions
- Wait for the in-flight analyze/embed job to finish, then retry the delete
- Poll the job status endpoint until it reports completed/failed before deleting
- If a stale lock persists after a crashed job, restart the GitNexus server process to release it
- Serialize operations: gate deletion behind completion of indexing jobs in your automation
Example fix
// before: delete while indexing
curl -X DELETE localhost:4747/api/repos/my-repo // 409 { error: lockErr }
// after: wait for the job first
const job = await res.json();
while ((await fetch(`/api/jobs/${job.id}`)).status !== 'completed') await sleep(1000);
curl -X DELETE localhost:4747/api/repos/my-repo // 200 Defensive patterns
Strategy: retry
Validate before calling
const jobs = await (await fetch('/api/jobs?repo=' + name)).json();
if (jobs.some(j => j.status === 'running')) throw new Error('job in flight; defer delete'); Try / catch
for (let i = 0; i < 5; i++) {
const res = await fetch(`/api/repos/${name}`, { method: 'DELETE' });
if (res.status !== 409) return res;
await sleep(2000 * (i + 1));
}
throw new Error('repo stayed locked; an analyze/embed job may be stuck'); Prevention
- Poll job status endpoints before deleting or re-indexing a repo
- Serialize automation so delete never overlaps analyze/embed
- Restart the server to clear locks left by crashed jobs
When it happens
Trigger: DELETE /api/repos/:name while an analyze or embed job is actively running against the same repo's storage path.
Common situations: Typical when CI kicks off a re-index and a cleanup script deletes repos concurrently, or when a user clicks delete in the web UI while a background analyze triggered by the CLI is still running.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Analysis already in progress
- Analysis already in progress
- Analysis already in progress
- Cannot verify acquisition/reclaim guard ownership
- LadybugDB unavailable for
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/7ea435bf527c008d.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/server/api.ts:1236
return;
}
let storagePath: string;
try {
storagePath = await requireDeletableStoragePath(entry);
} catch (err: any) {
if (err instanceof StorageDeletionError) {
res.status(400).json({ error: err.message });
return;
}
res.status(400).json({ error: err.message || 'Unsafe index storage path' });
return;
}
// Acquire repo lock — prevents deleting while analyze/embed is in flight
const lockKey = storagePath;
const lockErr = acquireRepoLock(lockKey);
if (lockErr) {
res.status(409).json({ error: lockErr });
return;
}
try {
// Close any open LadybugDB handle before deleting files
try {
await closeLbug();
} catch {}
// 1. Delete the .gitnexus index/storage directory
await fs.rm(storagePath, { recursive: true, force: true }).catch(() => {});
// 2. Delete the cloned repo dir if it lives under ~/.gitnexus/repos/.
// getCloneDir now throws on names that are not filesystem-safe (e.g.
// local repos registered with names like "my project" or "org/repo").
// Such repos legitimately have no clone dir, so treat the rejection as
// "nothing to clean up" rather than letting it fail the delete handler.
let cloneDir: string | null = null;View on GitHub (pinned to ac9a4e9abd)