ruvnet/ruflo · error · PodTemplateValidationError
budgetUsdPerRun must be ≥0
Error message
budgetUsdPerRun must be ≥0
What it means
The top-level budgetUsdPerRun number must be >= 0. As with the monthly field, zero is valid (no per-run cap) and only negative values are rejected here; non-numeric values fail earlier inside requireNumber with a different message.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:233
});
if (allowedMcpTools.length === 0) {
throw new PodTemplateValidationError('allowedMcpTools must have ≥1 entry', '/');
}
const bench = validatePodBench(json.bench, '/bench');
const piiPolicy = requireString(json, 'piiPolicy', '/');
if (!PII_POLICIES.includes(piiPolicy as PiiPolicy)) {
throw new PodTemplateValidationError(
`piiPolicy must be one of: ${PII_POLICIES.join(', ')}`,
'/',
);
}
const budgetUsdMonthly = requireNumber(json, 'budgetUsdMonthly', '/');
if (budgetUsdMonthly < 0) {
throw new PodTemplateValidationError('budgetUsdMonthly must be ≥0', '/');
}
const budgetUsdPerRun = requireNumber(json, 'budgetUsdPerRun', '/');
if (budgetUsdPerRun < 0) {
throw new PodTemplateValidationError('budgetUsdPerRun must be ≥0', '/');
}
if (budgetUsdMonthly > 0 && budgetUsdPerRun > budgetUsdMonthly) {
throw new PodTemplateValidationError(
'budgetUsdPerRun must not exceed budgetUsdMonthly',
'/',
);
}
const preferLocalExecution = requireBoolean(json, 'preferLocalExecution', '/');
const cronSchedule = requireString(json, 'cronSchedule', '/');
if (!CRON_RE.test(cronSchedule)) {
throw new PodTemplateValidationError(
'cronSchedule must be a POSIX cron expression (5 or 6 fields)',
'/',
);
}
const auditReadView = validateAuditReadView(json.auditReadView, '/auditReadView');
let reservationExpiryMs: number | undefined;View on GitHub (pinned to fa13ee4ad6)
Solutions
- Set budgetUsdPerRun to 0 for 'no per-run cap', or a positive dollar amount
- Check that the value is in USD (not cents) and positively signed
- Remember the follow-up constraint: if budgetUsdMonthly > 0, budgetUsdPerRun must not exceed it
Example fix
// before "budgetUsdPerRun": -0.01 // after "budgetUsdPerRun": 0.5
Defensive patterns
Strategy: validation
Validate before calling
if (typeof json.budgetUsdPerRun !== 'number' || json.budgetUsdPerRun < 0) {
json.budgetUsdPerRun = Math.max(0, Number(json.budgetUsdPerRun) || 0);
} Type guard
function isValidBudget(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /budgetUsdPerRun/.test(err.message)) {
// fix sign/unit, then also check per-run <= monthly when monthly > 0
}
} Prevention
- Keep both budget fields in USD dollars, never cents or credits
- After fixing per-run, re-run validation to catch the cross-field check next
When it happens
Trigger: A template with budgetUsdPerRun set to a negative number (e.g. -0.5). Reaching this specific message means requireNumber succeeded and the value is negative.
Common situations: Copy-paste from a 'credits remaining' style config where negatives were possible; converting cents to dollars with a sign slip; sentinel -1 'unlimited' conventions from other systems.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- budgetUsdMonthly must be ≥0
- pod-template at /: budgetUsdPerRun must not exceed budgetUsd
- bench.scheduleHours must be ≥1
- auditReadView must be an object
- includedEventTypes entries must be non-empty strings
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/5cb53184b413aef6.
Report an issue: GitHub.