abhigyanpatwari/GitNexus · warning
Job not found
Error message
Job not found
What it means
HTTP 404 returned by GET /api/analyze/:jobId when the id matches no tracked job. Job state lives in an in-memory Map inside the JobManager: records are lost when the serve process exits and are swept one hour (JOB_TTL_MS) after the job reaches a terminal state, with a cleanup sweep every 5 minutes. Ids are UUIDs minted by POST /api/analyze's 202 response.
Source
Thrown at gitnexus/src/server/api.ts:1676
// 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),
failJob: (jobId, error) => jobManager.updateJob(jobId, { status: 'failed', error }),
}),
);
// GET /api/analyze/:jobId — poll job status
app.get('/api/analyze/:jobId', (req, res) => {
const job = jobManager.getJob(req.params.jobId);
if (!job) {
res.status(404).json({ error: 'Job not found' });
return;
}
res.json({
id: job.id,
status: job.status,
repoUrl: job.repoUrl,
repoPath: job.repoPath,
repoName: job.repoName,
progress: job.progress,
error: job.error,
startedAt: job.startedAt,
completedAt: job.completedAt,
});
});
// GET /api/analyze/:jobId/progress — SSE stream (shared helper)
mountSSEProgress(app, '/api/analyze/:jobId/progress', jobManager);
View on GitHub (pinned to 0d1aed942f)
Solutions
- Treat 404 as 'job record gone', not 'analysis failed' — the finished index persists on disk independently; confirm via GET /api/repos that the repo is indexed
- If the repo is missing too, re-run POST /api/analyze to get a fresh jobId
- Consume jobIds promptly after the 202 and expect expiry roughly one hour after completion
- Persist the repo name/URL (stable identity) rather than the jobId
Example fix
// before
const job = await getJson(`/api/analyze/${jobId}`); // 404 after restart/expiry
// after
const res = await fetch(`/api/analyze/${jobId}`);
if (res.status === 404) {
const repos = await getJson('/api/repos');
const indexed = repos.some((r: any) => r.name === repoName);
if (!indexed) jobId = (await postAnalyze({ url: repoUrl })).jobId; // restart only if needed
} Defensive patterns
Strategy: fallback
Validate before calling
// Cheap pre-check: the id must be the UUID the 202 handed back
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_RE.test(jobId)) throw new Error(`jobId is not a valid job UUID: ${jobId}`); Type guard
const isJobId = (v: unknown): v is string =>
typeof v === 'string' &&
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v); Try / catch
On 404 from the poll endpoint, fall back to the durable outcome (GET /api/repos) and only re-trigger /api/analyze when the repo itself is missing — never loop-retry the same dead id.
Prevention
- Store repo identity, not jobIds, for anything long-lived
- Assume job records are ephemeral: in-memory, ~1h TTL after completion, gone on restart
- Keep the serve process alive while a UI polls, or re-issue analysis on 404
When it happens
Trigger: Polling a typo'd or truncated UUID; polling more than an hour after the job completed; polling a jobId from a previous server process (restart, crash, upgrade); ids persisted in a URL or localStorage across sessions.
Common situations: Dashboards that store jobIds long-term; page reloads after `gitnexus serve` was restarted; scripts resumed the next day; hand-copying ids and dropping characters.
Related errors
- Job not found
- Job already ${job.status}
- Analysis already in progress (job ${job.id})
- Analysis already in progress (job ${job.id})
- File not found
AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-08-20).
Data as JSON: /api/errors/2e8df4692c4d66b9.
Report an issue: GitHub.