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.dbView on GitHub (pinned to 6b01dc5a68)
Solutions
- Validate before calling: `if (!Number.isFinite(estimatedUsd) || estimatedUsd < 0) throw ...` or clamp to 0
- If the value comes from config/env, default to 0 or a sensible floor instead of passing NaN through
- 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
- Never pass the result of Number(undefined) or an unvalidated env/config value directly to reserve()
- Centralize cost computation in one function that returns a guaranteed finite non-negative number
- Unit-test the cost function with undefined, null, NaN, Infinity, and negative inputs
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
- actualUsd must be a non-negative finite number
- selectAgentBackend: pod.budgetUsdMonthly must be a finite nu
- pod-template at ${path}: field "${key}" must be a finite num
- pod-template at ${path}: bench.scheduleHours must be ≥1
- pod-template at ${path}: auditReadView.retentionDays must be
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/275241228879899d.
Report an issue: GitHub.