ruvnet/ruflo · error · Error

estimatedUsd must be a non-negative finite number

Error message

estimatedUsd must be a non-negative finite number

What it means

Input guard at the top of AtomicBbsRoomBudgetTracker.reserve(), run before the BEGIN IMMEDIATE transaction so a bad value never touches SQLite. Uses Number.isFinite() (excludes NaN and ±Infinity) plus a < 0 test. estimatedUsd is the projected cost the caller wants to reserve against the monthly cap; a malformed value would corrupt budget accounting or break the SQL CHECK constraint downstream.

Source

Thrown at v3/@claude-flow/cli/src/business-pods/bbs-budget-tracker.ts:193

        `INSERT INTO bbs_budget_rooms (room_id, monthly_cap_usd, billing_month, _lock_bump)
         VALUES (?, ?, ?, 0)
         ON CONFLICT(room_id) DO UPDATE SET monthly_cap_usd = excluded.monthly_cap_usd`,
      )
      .run(roomId, monthlyCapUsd, billingMonth);
  }

  /**
   * Atomically check budget and insert a reservation row in a single
   * BEGIN IMMEDIATE transaction. See ADR-164.1 §5.2.
   */
  reserve(
    roomId: string,
    callerId: string,
    estimatedUsd: number,
    opts?: { auditEnvelopeId?: string; expiryMs?: number },
  ): ReserveResult {
    if (!Number.isFinite(estimatedUsd) || estimatedUsd < 0) {
      throw new Error('estimatedUsd must be a non-negative finite number');
    }
    const auditEnvelopeId = opts?.auditEnvelopeId ?? `audit-${randomUUID()}`;
    const expiryMs = clampReservationExpiry(opts?.expiryMs ?? this.defaultExpiryMs);

    const nowMs = this.clock();
    const billingMonth = currentBillingMonth(nowMs);
    const billingMonthStart = billingMonthStartMs(billingMonth);

    const beginStmt = this.db.prepare('BEGIN IMMEDIATE');
    const commitStmt = this.db.prepare('COMMIT');
    const rollbackStmt = this.db.prepare('ROLLBACK');

    beginStmt.run();
    let transactionOpen = true;
    try {
      // Step 1: touch the room header row (the _lock_bump write makes the
      // lock acquisition visible per §3.2 peer-review note).
      const bumpRes = this.db

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Validate before calling: `if (!Number.isFinite(estimatedUsd) || estimatedUsd < 0) throw ...` or clamp to 0
  2. If the value comes from config/env, default to 0 or a sensible floor instead of passing NaN through
  3. Replace sentinel values like -1 with explicit null and skip the reservation rather than reserving a negative amount

Example fix

// before:
const est = Number(process.env.POD_TICK_COST); // NaN when unset
tracker.reserve(roomId, callerId, est); // throws

// after:
const raw = Number(process.env.POD_TICK_COST);
const est = Number.isFinite(raw) && raw >= 0 ? raw : 0;
tracker.reserve(roomId, callerId, est);
Defensive patterns

Strategy: validation

Validate before calling

function safeReservationAmount(estimatedUsd: unknown): number {
  const n = typeof estimatedUsd === 'number' ? estimatedUsd : Number(estimatedUsd);
  if (!Number.isFinite(n) || n < 0) return 0; // or throw, depending on policy
  return n;
}
// before calling reserve():
const amount = safeReservationAmount(rawCost);
tracker.reserve(roomId, callerId, amount);

Type guard

function isValidReservationAmount(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}

Prevention

When it happens

Trigger: Calling tracker.reserve(roomId, callerId, estimatedUsd) where estimatedUsd is NaN (e.g., Number(undefined) or undefined * 1), ±Infinity (division by zero), or a negative number.

Common situations: Cost estimate read from env via Number(process.env.X) where the var is unset (NaN); a pricing/cost model returning -1 as a sentinel for unknown; a parseFloat on garbage user input; a division producing Infinity when a denominator is zero.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/275241228879899d. Report an issue: GitHub.