ruvnet/ruflo · error · PodTemplateValidationError
pod-template at ${path}: budgetUsdPerRun must not exceed bud
Error message
pod-template at ${path}: budgetUsdPerRun must not exceed budgetUsdMonthly What it means
Thrown by validatePodTemplate() when budgetUsdPerRun exceeds budgetUsdMonthly AND budgetUsdMonthly is greater than 0. A single run should never cost more than the entire monthly cap (when a cap is set). When budgetUsdMonthly is 0 (unlimited), this check is skipped. The error message includes the 'pod-template at /:' prefix added by PodTemplateValidationError's constructor.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:236
}
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;
if (json.reservationExpiryMs !== undefined) {
const v = requireNumber(json, 'reservationExpiryMs', '/');
// ADR-164.1 §3.2 — bounded to [5_000, 300_000] ms.View on GitHub (pinned to 6b01dc5a68)
Solutions
- Lower budgetUsdPerRun to be less than or equal to budgetUsdMonthly
- Or raise budgetUsdMonthly to accommodate the per-run cost
- Or set budgetUsdMonthly to 0 (unlimited) if the monthly cap is intentionally not enforced
Example fix
// before
{ "budgetUsdMonthly": 100, "budgetUsdPerRun": 150 }
// after
{ "budgetUsdMonthly": 500, "budgetUsdPerRun": 150 } Defensive patterns
Strategy: validation
Validate before calling
if (template.budgetUsdMonthly > 0 && template.budgetUsdPerRun > template.budgetUsdMonthly) {
throw new Error('budgetUsdPerRun must not exceed budgetUsdMonthly (or set monthly to 0 for unlimited)');
} Type guard
function budgetsAreConsistent(monthly: number, perRun: number): boolean {
return monthly === 0 || perRun <= monthly;
} Try / catch
try {
validatePodTemplate(json);
} catch (e) {
if (e instanceof PodTemplateValidationError && e.message.includes('budgetUsdPerRun must not exceed')) {
// Either raise budgetUsdMonthly or lower budgetUsdPerRun
}
} Prevention
- Reconcile per-run and monthly budgets before deployment
- Set budgetUsdMonthly to 0 to disable the monthly cap
- Watch for decimal-point errors: 5.00 vs 50.00
When it happens
Trigger: budgetUsdMonthly is a positive number (e.g. 100) and budgetUsdPerRun is larger (e.g. 150). For example, monthly=500, perRun=600 triggers it; monthly=0, perRun=999 does not.
Common situations: Per-run estimate was computed independently from the monthly cap and the two were never reconciled; a decimal point error (e.g. perRun=50.00 vs monthly=5.00).
Related errors
- pod-template at ${path}: budgetUsdMonthly must be ≥0
- pod-template at ${path}: budgetUsdPerRun must be ≥0
- budgetUsdPerRun must not exceed budgetUsdMonthly
- reservationExpiryMs must be within [5000, 300000] ms (ADR-16
- pod-template at ${path}: field "${key}" must be a non-empty
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/68badd1490629452.
Report an issue: GitHub.