All Docs
FeaturesAgentOS WorkUpdated March 11, 2026

Bug Fix: Payroll Variance Alert Was Scanning All-Time Paid Runs

Bug Fix: Payroll Variance Alert Was Scanning All-Time Paid Runs

Version: 1.0.17 Component: Payroll Engine — payrollVarianceAlert (Inngest cron function) Severity: Medium — incorrect alert scope, no data loss


What Happened

The payrollVarianceAlert scheduled function is designed to detect unusual payroll variances across runs completed in the past 7 days and notify the relevant stakeholders. In versions prior to 1.0.17, a logic error in the database query meant the 7-day window was never actually applied.

The function correctly computed a cutoff timestamp representing seven days ago, but that value was never passed into the Drizzle ORM .where() clause. Instead, the query contained two redundant conditions that both checked only payrollRuns.status = 'paid' — one using eq and one using inArray on the same column with the same single value. The inArray predicate was a no-op, and the date restriction was dead code.

The practical effect: every execution of the cron job fetched up to 200 paid payroll runs from all time, not just the past week. Variance calculations and alerts were therefore based on a potentially much larger and older dataset than intended.


Who Was Affected

All tenants using the automated payroll variance alerting feature. The impact varied depending on how much historical payroll data existed in the system:

  • Newer tenants with limited history: minimal observable difference.
  • Established tenants with many months of payroll history: alerts may have been skewed by or triggered on old data, and the 200-row cap may have excluded recent runs entirely in favour of older ones (depending on sort order).

The Fix

The .where() clause in src/inngest/functions/payroll-engine.ts (lines 193–220) has been corrected:

  1. Removed the redundant inArray(payrollRuns.status, ['paid']) condition.
  2. Added gte(payrollRuns.processedAt, cutoff) to enforce the 7-day lookback using the already-computed cutoff variable.
  3. Imported the gte comparator from drizzle-orm.
// v1.0.17 — corrected query
import { and, eq, gte } from 'drizzle-orm';

// Inside payrollVarianceAlert handler:
const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);

const runs = await db
  .select()
  .from(payrollRuns)
  .where(
    and(
      eq(payrollRuns.status, 'paid'),
      gte(payrollRuns.processedAt, cutoff)
    )
  )
  .limit(200);

The query now returns only paid runs whose processedAt timestamp falls within the past 7 days, which is the intended behaviour.


What You Should Do

No action is required. The fix is deployed automatically with v1.0.17. Variance alerts going forward will correctly reflect only the trailing 7-day window.

If you received payroll variance alerts in recent weeks that seemed inconsistent with your current payroll data, those may have been influenced by historical runs outside the intended window. We recommend reviewing any alerts generated before this release against your payroll records directly.


Technical Reference

DetailValue
Filesrc/inngest/functions/payroll-engine.ts
FunctionpayrollVarianceAlert
Lines affected193–220
ORMDrizzle ORM
Fix typeQuery predicate correction + dead code removal
Breaking changeNo