abhigyanpatwari/GitNexus · warning
Job not found
Error message
Job not found
What it means
HTTP 404 (a JSON body, not an event-stream) returned by the SSE progress endpoints — /api/analyze/:jobId/progress and /api/embed/:jobId/progress, both mounted through mountSSEProgress — when the jobId matches no tracked job. The job lifecycle is the same as the poll endpoints: in-memory records, lost on restart, swept ~1h after terminal state. A non-string jobId parameter instead yields 400 from the assertString check before this point.
Source
Thrown at gitnexus/src/server/sse-progress.ts:73
*
* `updateJob` already synthesizes exactly one terminal progress event (and
* refuses every update once the job is terminal, #2264 P3), and it assigns the
* status BEFORE emitting — so asking the job is both sufficient and the only
* source that cannot be spoofed by an intermediate phase label.
*/
export const mountSSEProgress = (app: express.Express, routePath: string, jm: JobManager) => {
app.get(routePath, (req, res) => {
let jobId: string;
try {
jobId = assertString(req.params.jobId, 'jobId');
} catch (err) {
const status = err instanceof BadRequestError ? err.status : 400;
res.status(status).json({ error: err instanceof Error ? err.message : 'Invalid jobId' });
return;
}
const job = jm.getJob(jobId);
if (!job) {
res.status(404).json({ error: 'Job not found' });
return;
}
let eventId = 0;
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'X-Accel-Buffering': 'no',
});
// Send current state immediately
eventId++;
res.write(`id: ${eventId}\ndata: ${JSON.stringify(job.progress)}\n\n`);
// If already terminal, send event and close
if (isTerminalJobStatus(job.status)) {
eventId++;View on GitHub (pinned to aac7515d2a)
Solutions
- Probe the JSON poll endpoint (GET /api/analyze/:jobId) when the EventSource errors — 404 there too means the record is gone: re-trigger analysis if needed and reconnect with the fresh id
- Open the stream immediately after the 202 and close it when a terminal event arrives
- Persist repo identity rather than jobIds; re-POST to mint a new id after restarts
- Use the full UUID from the 202 response in the URL
Example fix
// before
const es = new EventSource(`/api/analyze/${jobId}/progress`); // errors on 404, no recovery
// after
const es = new EventSource(`/api/analyze/${jobId}/progress`);
es.onerror = () => {
fetch(`/api/analyze/${jobId}`).then(async (r) => {
if (r.status === 404) {
const { jobId: fresh } = await postAnalyze(body); // restart and reconnect
reconnect(`/api/analyze/${fresh}/progress`);
}
});
}; Defensive patterns
Strategy: fallback
Validate before calling
// Validate the id before opening the stream
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(`Invalid jobId: ${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
EventSource has no catch — handle onerror: probe the poll endpoint, and on 404 fall back to re-triggering the job and reconnecting with the new id instead of letting the browser retry the dead URL forever.
Prevention
- Open SSE streams right after receiving the 202
- Handle onerror explicitly — a 404 stream never self-heals
- Store repo identity, not jobIds, across restarts
When it happens
Trigger: Opening an EventSource for an expired (post-TTL) or never-existing job; reconnecting an SSE stream after serve restarted; refreshing a progress page whose URL holds an old jobId; a truncated or typo'd UUID.
Common situations: Long-lived progress tabs reconnecting after server upgrades; jobIds kept in URL/session state; EventSource auto-reconnect racing job cleanup.
Related errors
- Job not found
- Job already ${job.status}
- Analysis already in progress (job ${job.id})
- Analysis already in progress (job ${job.id})
- LLM returned empty streaming response
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/6b3062da2cd38a44.
Report an issue: GitHub.