thedotmack/claude-mem · error
illegal observation generation job transition from ${current
Error message
illegal observation generation job transition from ${current.status} to ${nextStatus} What it means
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.
Source
Thrown at src/storage/postgres/generation-jobs.ts:407
const ALLOWED_JOB_TRANSITIONS: Record<ObservationGenerationJobStatus, readonly ObservationGenerationJobStatus[]> = {
queued: ['processing', 'failed', 'cancelled'],
processing: ['queued', 'completed', 'failed', 'cancelled'],
completed: [],
failed: [],
cancelled: []
};
function assertValidJobStatusTransition(
current: PostgresObservationGenerationJob,
nextStatus: ObservationGenerationJobStatus
): void {
if (TERMINAL_JOB_STATUSES.has(current.status)) {
throw new Error(`cannot transition observation generation job from terminal status ${current.status}`);
}
if (!ALLOWED_JOB_TRANSITIONS[current.status].includes(nextStatus)) {
throw new Error(`illegal observation generation job transition from ${current.status} to ${nextStatus}`);
}
if (nextStatus === 'processing' && current.attempts >= current.maxAttempts) {
throw new Error('cannot process observation generation job after max_attempts is reached');
}
if (nextStatus === 'queued' && current.attempts >= current.maxAttempts) {
throw new Error('cannot retry observation generation job after max_attempts is reached');
}
}
function mapJobRow(row: JobRow): PostgresObservationGenerationJob {
return {
id: row.id,
projectId: row.project_id,
teamId: row.team_id,
agentEventId: row.agent_event_id,
sourceType: row.source_type,View on GitHub (pinned to d768ba3643)
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).
Example fix
// before
await jobs.transitionStatus({ id, projectId, teamId, status:'completed' }); // current is queued
// after
await jobs.transitionStatus({ id, projectId, teamId, status:'processing' });
// ... do work ...
await jobs.transitionStatus({ id, projectId, teamId, status:'completed' }); Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = { queued:['processing','failed','cancelled'], processing:['queued','completed','failed','cancelled'], completed:[], failed:[], cancelled:[] };
function canTransition(from, to) { return ALLOWED[from].includes(to); }
if (!canTransition(current.status, desired)) throw new Error(`unsupported ${current.status}->${desired}`); Type guard
function isLegalTransition(from, to) { const m={queued:['processing','failed','cancelled'],processing:['queued','completed','failed','cancelled'],completed:[],failed:[],cancelled:[]}; return m[from]?.includes(to) ?? false; } Prevention
- Keep a copy of the transition matrix next to your worker code.
- Always pass through processing on the way to completed.
When it happens
Trigger: Examples: transitioning queued -> completed directly (skipping processing), queued -> queued, or processing -> processing. Any transition not explicitly listed is illegal.
Common situations: Worker short-circuiting processing and going straight from queued to completed; calling queued->queued to 'reset'; misordered state updates from concurrent workers.
Related errors
- cannot transition observation generation job from terminal s
- cannot process observation generation job after max_attempts
- cannot retry observation generation job after max_attempts i
- server_session_id must match the agent_event server_session_
- session_summary source_id must equal server_session_id
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/6acc946bc734ae11.
Report an issue: GitHub.