thedotmack/claude-mem · error
cannot retry observation generation job after max_attempts i
Error message
cannot retry observation generation job after max_attempts is reached
What it means
Thrown by assertValidJobStatusTransition when nextStatus is 'queued' (i.e. a retry/back-to-queue) and current.attempts >= current.maxAttempts. Re-queueing for another attempt is not allowed once the budget is spent. Only reachable from 'processing' -> 'queued'.
Source
Thrown at src/storage/postgres/generation-jobs.ts:415
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,
sourceId: row.source_id,
serverSessionId: row.server_session_id,
jobType: row.job_type,
status: row.status,
idempotencyKey: row.idempotency_key,
bullmqJobId: row.bullmq_job_id,
attempts: row.attempts,
maxAttempts: row.max_attempts,View on GitHub (pinned to d768ba3643)
Solutions
- Check attempts < maxAttempts before scheduling a retry (transition to 'queued').
- Transition to 'failed' with lastError once the budget is exhausted.
- Raise maxAttempts on a new job if more attempts are truly required.
Example fix
// before
await jobs.transitionStatus({ id, projectId, teamId, status:'queued', nextAttemptAt: backoffDate });
// after
if (job.attempts >= job.maxAttempts) {
await jobs.transitionStatus({ id, projectId, teamId, status:'failed', lastError });
} else {
await jobs.transitionStatus({ id, projectId, teamId, status:'queued', nextAttemptAt: backoffDate });
} Defensive patterns
Strategy: validation
Validate before calling
if (job.attempts >= job.maxAttempts) { await jobs.transitionStatus({ id, projectId, teamId, status:'failed', lastError }); return; } Type guard
function canRetry(job){ return job.attempts < job.maxAttempts; } Prevention
- Gate every retry schedule on attempts < maxAttempts.
When it happens
Trigger: Calling transitionStatus({status:'queued'}) on a processing job that already used all attempts (a retry-schedule path). The DB UPDATE blocks it and this guard explains why.
Common situations: Backoff scheduler naively re-queuing every failed attempt; transient error handler scheduling retries without checking remaining budget; concurrent retry schedulers.
Related errors
- cannot process observation generation job after max_attempts
- cannot transition observation generation job from terminal s
- illegal observation generation job transition from ${current
- 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/a023ce5aafeded3b.
Report an issue: GitHub.