abhigyanpatwari/GitNexus · warning
Analysis already in progress (job ${job.id})
Error message
Analysis already in progress (job ${job.id}) What it means
HTTP 409 returned by POST /api/analyze when JobManager.createJob throws `Analysis already in progress (job <uuid>)` (analyze-job.ts:114) — its single-slot rule: the manager runs one active job at a time, and a non-terminal job for a DIFFERENT repository already exists (queued/cloning/analyzing). The route maps any message containing 'already in progress' to 409. The same-repo case is not an error: createJob dedupes it and the route answers 202 with the existing jobId (plus a tokenIgnored note). /api/analyze/upload shares the same manager, so an upload analysis also occupies the slot.
Source
Thrown at gitnexus/src/server/api.ts:1609
if (!targetPath) {
throw new Error('No target path resolved');
}
launchAnalysisWorker(job, targetPath, { force, embeddings, dropEmbeddings });
} catch (err: any) {
if (targetPath) releaseRepoLock(getStoragePath(targetPath));
jobManager.updateJob(job.id, {
status: 'failed',
error: err.message || 'Analysis failed',
});
}
})();
res.status(202).json({ jobId: job.id, status: job.status });
} catch (err: any) {
if (err.message?.includes('already in progress')) {
res.status(409).json({ error: err.message });
} else {
res.status(500).json({ error: err.message || 'Failed to start analysis' });
}
}
},
);
// POST /api/analyze/upload — analyze a browser folder upload.
// Securely ingests the multipart upload into a sandbox, promotes it to a
// persistent dir, and analyzes it via the shared job/worker machinery.
// localhost-only (no cross-origin write reach) + conservative rate limit.
app.post(
'/api/analyze/upload',
createRouteLimiter({ limit: 5 }),
requireTrustedOrigin,
createAnalyzeUploadHandler({
createJob: (params) => jobManager.createJob(params),
launch: (job, targetPath, opts) => launchAnalysisWorker(job, targetPath, opts),View on GitHub (pinned to aac7515d2a)
Solutions
- Adopt the running job: parse its id from the 409 message (`job <uuid>`) and poll GET /api/analyze/:jobId (or its /progress SSE stream) instead of creating a new one
- Serialize client-side — the manager allows exactly one active analysis, so queue repos and start the next only after the previous reaches complete/failed
- Re-posting the SAME repo is safe: you get a 202 dedup with the existing id, not a 409
- If the slot seems stuck, poll the active job — a hung worker eventually times out (30-minute job timeout) and frees the slot
Example fix
// before
const res = await fetch('/api/analyze', { method: 'POST' /* ... */ });
if (!res.ok) throw new Error(await res.text()); // 409 crashes the flow
// after
const res = await fetch('/api/analyze', { method: 'POST' /* ... */ });
if (res.status === 409) {
const { error } = await res.json();
const existingId = error.match(/job ([0-9a-f-]{36})/)?.[1];
if (existingId) await pollUntilTerminal(`/api/analyze/${existingId}`); // wait it out
} else if (!res.ok) throw new Error((await res.json()).error); Defensive patterns
Strategy: fallback
Validate before calling
// Single-flight: concurrent callers reuse one POST instead of colliding
let inflight: Promise<Response> | null = null;
async function postAnalyze(body: object): Promise<Response> {
if (inflight) return inflight;
const p = fetch('/api/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
inflight = p;
try { return await p; } finally { if (inflight === p) inflight = null; }
} Try / catch
Catch HTTP 409 specifically, extract the active job id from `job <uuid>` in the message, and fall back to polling that job (GET /api/analyze/:jobId or the SSE stream). Do not retry the POST unchanged while the other job is active — it will keep conflicting.
Prevention
- Treat 202 as 'started', never 'done' — poll to complete/failed before starting the next repo
- Serialize multi-repo batches; the JobManager is single-slot by design
- Reuse the same-repo 202 dedup instead of re-POSTing
- Disable submit actions in the UI while a job is active
When it happens
Trigger: POST /api/analyze for repo B while repo A's analysis (or upload analysis) is still running; a retry double-click racing a slow clone; batch scripts iterating repos without waiting for terminal status; UIs firing a second POST before polling the first.
Common situations: Treating the 202 as completion and re-POSTing; parallel CI jobs against one serve instance; dashboards queuing multiple repos simultaneously; very large clones holding the single slot for a long time.
Related errors
- Analysis already in progress (job ${job.id})
- Analysis already in progress (job ${job.id})
- Failed to start analysis
- GitNexus: unable to acquire init lock after ${INIT_LOCK_MAX_
- LadybugDB unavailable for ${repoId}. Another process may be
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/f184260172f079c9.
Report an issue: GitHub.