ruvnet/ruflo · error · PodTemplateValidationError
pod-template at /: reservationExpiryMs must be within [5000,
Error message
pod-template at /: reservationExpiryMs must be within [5000, 300000] ms (ADR-164.1 §3.2)
What it means
reservationExpiryMs is an optional field; when present it is bounded to [5000, 300000] ms (5 s to 5 min) per ADR-164.1 §3.2 — the reservation/lease window must be long enough to be usable but short enough that a crashed holder cannot stall a pod for long. Values below 5 s or above 300000 ms are rejected at path '/'. The 'pod-template at /:' prefix is the caller's path formatting of the thrown message.
Source
Thrown at v3/@claude-flow/cli/src/business-pods/pod-schema.ts:256
'/',
);
}
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.
if (v < 5_000 || v > 300_000) {
throw new PodTemplateValidationError(
'reservationExpiryMs must be within [5000, 300000] ms (ADR-164.1 §3.2)',
'/',
);
}
reservationExpiryMs = v;
}
return {
name,
displayName,
roomId,
agents,
allowedMcpTools,
bench,
piiPolicy: piiPolicy as PiiPolicy,
budgetUsdMonthly,
budgetUsdPerRun,
preferLocalExecution,View on GitHub (pinned to fa13ee4ad6)
Solutions
- Pick a value in [5000, 300000], e.g. 30000 (30 s) or 60000 (60 s)
- If you wanted 'no expiry', that is not supported — use the 300000 ms ceiling instead
- Double-check the unit is milliseconds (30 seconds = 30000, not 30)
Example fix
// before "reservationExpiryMs": 10 // after "reservationExpiryMs": 30000
Defensive patterns
Strategy: validation
Validate before calling
if (json.reservationExpiryMs !== undefined) {
const v = json.reservationExpiryMs;
if (typeof v !== 'number' || v < 5000 || v > 300000) {
json.reservationExpiryMs = 30000; // safe default inside ADR-164.1 §3.2 bounds
}
} Type guard
function isValidReservationExpiry(v: unknown): boolean {
return v === undefined || (typeof v === 'number' && v >= 5000 && v <= 300000);
} Try / catch
try { validatePodTemplate(json); } catch (err) {
if (err instanceof PodTemplateValidationError && /reservationExpiryMs/.test(err.message)) {
// clamp into [5000, 300000] ms or delete the field to use the default
}
} Prevention
- Express the value in milliseconds and sanity-check against 5 s / 5 min bounds
- Omit the field when the default lease window is acceptable
When it happens
Trigger: A template with reservationExpiryMs: 1000 (1 s — too short) or 3600000 (1 h — too long). Omitting the field entirely is fine and falls back to the default; only explicitly supplied out-of-range values throw.
Common situations: Porting lease timeouts tuned for other systems; unit confusion — expressing seconds instead of milliseconds (10 meaning '10 seconds' reads as 10 ms); attempting to 'disable' expiry with a huge value like Number.MAX_SAFE_INTEGER.
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
- bench.scheduleHours must be ≥1
- auditReadView must be an object
- includedEventTypes entries must be non-empty strings
- auditReadView.retentionDays must be ≥1
- roomId may only contain [A-Za-z0-9_.\-:/@#]
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/b317872e9e338e6f.
Report an issue: GitHub.