From 29ff83cfd69418f77fc74a7c1b368a160f89e05d Mon Sep 17 00:00:00 2001 From: bot-hermes Date: Thu, 6 Aug 2026 13:47:15 +0000 Subject: [PATCH] fix(worker): honor jobs.maxAttempts and treat NULL nextRetryAt as due --- worker/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/worker/index.ts b/worker/index.ts index 370a362..6681499 100644 --- a/worker/index.ts +++ b/worker/index.ts @@ -1,5 +1,5 @@ import { db, jobs, webhooks, webhookDeliveries, scheduledJobs, tasks, habits, habitCompletions } from '@project-e/db'; -import { and, eq, lte, isNull, sql } from 'drizzle-orm'; +import { and, eq, lte, isNull, or } from 'drizzle-orm'; import { createHmac } from 'node:crypto'; import rrule from 'rrule'; const { RRule } = rrule; @@ -21,12 +21,13 @@ async function poll(): Promise { try { const now = new Date(); - // Get pending jobs that are due + // Get pending jobs that are due. + // nextRetryAt is NULL for freshly-queued jobs, which are due immediately. const pendingJobs = await db.select() .from(jobs) .where(and( eq(jobs.status, 'pending'), - lte(jobs.nextRetryAt ?? sql`now()`, now), + or(isNull(jobs.nextRetryAt), lte(jobs.nextRetryAt, now)), )) .orderBy(jobs.createdAt) .limit(10); @@ -90,8 +91,9 @@ async function processJob(job: typeof jobs.$inferSelect): Promise { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const attempts = (job.attempts || 0) + 1; + const maxAttempts = job.maxAttempts || MAX_RETRIES; - if (attempts >= MAX_RETRIES) { + if (attempts >= maxAttempts) { // Max retries reached — mark as failed await db.update(jobs) .set({