{"record":{"id":"6acc946bc734ae11","repo":"thedotmack/claude-mem","slug":"illegal-observation-generation-job-transition-from","errorCode":null,"errorMessage":"illegal observation generation job transition from ${current.status} to ${nextStatus}","messagePattern":"illegal observation generation job transition from (.+?) to (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/storage/postgres/generation-jobs.ts","lineNumber":407,"sourceCode":"\nconst ALLOWED_JOB_TRANSITIONS: Record<ObservationGenerationJobStatus, readonly ObservationGenerationJobStatus[]> = {\n  queued: ['processing', 'failed', 'cancelled'],\n  processing: ['queued', 'completed', 'failed', 'cancelled'],\n  completed: [],\n  failed: [],\n  cancelled: []\n};\n\nfunction assertValidJobStatusTransition(\n  current: PostgresObservationGenerationJob,\n  nextStatus: ObservationGenerationJobStatus\n): void {\n  if (TERMINAL_JOB_STATUSES.has(current.status)) {\n    throw new Error(`cannot transition observation generation job from terminal status ${current.status}`);\n  }\n\n  if (!ALLOWED_JOB_TRANSITIONS[current.status].includes(nextStatus)) {\n    throw new Error(`illegal observation generation job transition from ${current.status} to ${nextStatus}`);\n  }\n\n  if (nextStatus === 'processing' && current.attempts >= current.maxAttempts) {\n    throw new Error('cannot process observation generation job after max_attempts is reached');\n  }\n\n  if (nextStatus === 'queued' && current.attempts >= current.maxAttempts) {\n    throw new Error('cannot retry observation generation job after max_attempts is reached');\n  }\n}\n\nfunction mapJobRow(row: JobRow): PostgresObservationGenerationJob {\n  return {\n    id: row.id,\n    projectId: row.project_id,\n    teamId: row.team_id,\n    agentEventId: row.agent_event_id,\n    sourceType: row.source_type,","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/storage/postgres/generation-jobs.ts#L389-L425","documentation":"Thrown by assertValidJobStatusTransition when the current status is non-terminal but the requested nextStatus is not in ALLOWED_JOB_TRANSITIONS[current]. The matrix is: queued -> {processing,failed,cancelled}; processing -> {queued,completed,failed,cancelled}; the terminal statuses have no outgoing edges.","triggerScenarios":"Examples: transitioning queued -> completed directly (skipping processing), queued -> queued, or processing -> processing. Any transition not explicitly listed is illegal.","commonSituations":"Worker short-circuiting processing and going straight from queued to completed; calling queued->queued to 'reset'; misordered state updates from concurrent workers.","solutions":["Route through 'processing' before 'completed' (queued -> processing -> completed).","Audit the requested nextStatus against ALLOWED_JOB_TRANSITIONS before calling transitionStatus.","If you only need to cancel a queued job, use status 'cancelled' (allowed from queued)."],"exampleFix":"// before\nawait jobs.transitionStatus({ id, projectId, teamId, status:'completed' }); // current is queued\n// after\nawait jobs.transitionStatus({ id, projectId, teamId, status:'processing' });\n// ... do work ...\nawait jobs.transitionStatus({ id, projectId, teamId, status:'completed' });","handlingStrategy":"validation","validationCode":"const ALLOWED = { queued:['processing','failed','cancelled'], processing:['queued','completed','failed','cancelled'], completed:[], failed:[], cancelled:[] };\nfunction canTransition(from, to) { return ALLOWED[from].includes(to); }\nif (!canTransition(current.status, desired)) throw new Error(`unsupported ${current.status}->${desired}`);","typeGuard":"function isLegalTransition(from, to) { const m={queued:['processing','failed','cancelled'],processing:['queued','completed','failed','cancelled'],completed:[],failed:[],cancelled:[]}; return m[from]?.includes(to) ?? false; }","tryCatchPattern":null,"preventionTips":["Keep a copy of the transition matrix next to your worker code.","Always pass through processing on the way to completed."],"tags":["postgres","state-machine","generation-jobs","validation"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}