diff --git a/apps/sim/app/api/cron/cleanup-stale-executions/route.ts b/apps/sim/app/api/cron/cleanup-stale-executions/route.ts index c8b74944dbe..0f5f62074d4 100644 --- a/apps/sim/app/api/cron/cleanup-stale-executions/route.ts +++ b/apps/sim/app/api/cron/cleanup-stale-executions/route.ts @@ -245,10 +245,14 @@ export const GET = withRouteHandler(async (request: NextRequest) => { = (${asyncJobs.metadata}->>'maxDurationSeconds')::numeric ELSE FALSE END` + // A bare `Date` in a raw template reaches the driver unserialized; `sql.param` + // binds it through the column encoder, as `lt(column, date)` does elsewhere here. + const staleProcessingCutoff = sql.param(now, asyncJobs.startedAt) + const staleProcessingFallbackCutoff = sql.param(staleThreshold, asyncJobs.startedAt) const staleProcessingDurationPredicate = sql`CASE WHEN ${hasPositiveMaxDuration} - THEN ${asyncJobs.startedAt} + ((${asyncJobs.metadata}->>'maxDurationSeconds')::double precision * interval '1 second') < ${now} - ELSE ${asyncJobs.startedAt} < ${staleThreshold} + THEN ${asyncJobs.startedAt} + ((${asyncJobs.metadata}->>'maxDurationSeconds')::double precision * interval '1 second') < ${staleProcessingCutoff} + ELSE ${asyncJobs.startedAt} < ${staleProcessingFallbackCutoff} END` const staleProcessingPredicate = and( eq(asyncJobs.status, JOB_STATUS.PROCESSING), diff --git a/packages/testing/src/mocks/database.mock.ts b/packages/testing/src/mocks/database.mock.ts index 455c90f66c8..d246ba9d603 100644 --- a/packages/testing/src/mocks/database.mock.ts +++ b/packages/testing/src/mocks/database.mock.ts @@ -6,6 +6,16 @@ import { vi } from 'vitest' */ export function createMockSql() { const sqlFn = (strings: TemplateStringsArray, ...values: any[]) => { + // Same hazard as `sql.param(date)` below, and the form that actually shipped: + // an interpolated `Date` carries no column context, so drizzle skips + // `PgTimestamp.mapToDriverValue` and postgres-js receives a Date it cannot serialize. + if (values.some((value) => value instanceof Date)) { + throw new Error( + 'sql`…${date}` interpolates a Date without an encoder, which reaches ' + + 'postgres-js as a Date object its unsafe path cannot serialize. Bind ' + + 'through the matching column: sql.param(date, table.timestampColumn).' + ) + } const fragment = { strings, values,