ruvnet/ruflo · error · PodTemplateValidationError
budgetUsdMonthly must be ≥0
Error message
budgetUsdMonthly must be ≥0
What it means
The top-level budgetUsdMonthly number must be >= 0. Negative budgets are meaningless; zero is allowed and means 'no monthly cap' (note: the cross-field per-run check in error 193 only applies when budgetUsdMonthly > 0). requireNumber already rejected non-numbers, so this error is specifically a negative numeric value.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:229
tp,
);
}
return t;
});
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)',
'/',
);View on GitHub (pinned to fa13ee4ad6)
Solutions
- Use 0 to express 'no monthly budget' — it is the schema's intended unlimited/no-cap value
- Fix sign errors: any positive USD amount is valid
- If porting configs that use -1 for unlimited, translate -1 -> 0 in your template converter
Example fix
// before "budgetUsdMonthly": -1 // after "budgetUsdMonthly": 0
Defensive patterns
Strategy: validation
Validate before calling
if (typeof json.budgetUsdMonthly !== 'number' || json.budgetUsdMonthly < 0) {
json.budgetUsdMonthly = Math.max(0, Number(json.budgetUsdMonthly) || 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 && /budgetUsdMonthly/.test(err.message)) {
// map -1 'unlimited' sentinels to 0 and re-validate
}
} Prevention
- Translate -1 'unlimited' sentinels to 0 when importing external configs
- Validate budget fields with the same >= 0 rule in your template generator
When it happens
Trigger: A template with budgetUsdMonthly set to a negative number, e.g. -1 or -100. A string like "-5" would instead fail requireNumber with a missing/invalid-field error.
Common situations: Using -1 as a 'no budget / unlimited' sentinel the way other config systems do; sign typos; spreadsheet exports producing negative placeholder values.
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
- budgetUsdPerRun 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/b993f0e9d1679de3.
Report an issue: GitHub.