ruvnet/ruflo · error · TypeError

selectAgentBackend: pod.budgetUsdMonthly must be a finite nu

Error message

selectAgentBackend: pod.budgetUsdMonthly must be a finite number (validate via pod-schema first)

What it means

TypeError in selectAgentBackend() when pod.budgetUsdMonthly is missing, not a number, or not finite (NaN/±Infinity). This field is the threshold input for the routing decision (>= 50 routes to cloud-managed). A malformed value means the pod was not validated via validatePodTemplate() which enforces a finite number.

Source

Thrown at v3/@claude-flow/cli/src/business-pods/domain-affinity-policy.ts:61

/**
 * Decide which backend `@metaharness/router` should prefer for a pod tick.
 *
 * @param pod  A validated PodTemplate. Validation is the caller's
 *             responsibility; this function will throw on malformed input
 *             (specifically on missing `preferLocalExecution` or
 *             `budgetUsdMonthly`).
 */
export function selectAgentBackend(pod: PodTemplate): BackendDecision {
  if (pod === null || typeof pod !== 'object') {
    throw new TypeError('selectAgentBackend: pod must be a validated PodTemplate object');
  }
  if (typeof pod.preferLocalExecution !== 'boolean') {
    throw new TypeError(
      'selectAgentBackend: pod.preferLocalExecution must be boolean (validate via pod-schema first)',
    );
  }
  if (typeof pod.budgetUsdMonthly !== 'number' || !Number.isFinite(pod.budgetUsdMonthly)) {
    throw new TypeError(
      'selectAgentBackend: pod.budgetUsdMonthly must be a finite number (validate via pod-schema first)',
    );
  }

  if (pod.preferLocalExecution) {
    return {
      backend: 'local-stdio',
      reason: `pod "${pod.name}" preferLocalExecution=true — domain-affinity policy pins to local stdio`,
    };
  }
  if (pod.budgetUsdMonthly >= CLOUD_BUDGET_THRESHOLD_USD) {
    return {
      backend: 'cloud-managed',
      reason:
        `pod "${pod.name}" preferLocalExecution=false and budgetUsdMonthly=${pod.budgetUsdMonthly} ` +
        `>= ${CLOUD_BUDGET_THRESHOLD_USD} — routes to cloud Managed Agents`,
    };
  }

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Run validatePodTemplate() — requireNumber() checks typeof === 'number' and Number.isFinite() before selectAgentBackend ever sees the value
  2. If constructing the object in code, ensure budgetUsdMonthly is a finite number literal

Example fix

// before:
selectAgentBackend({ ...pod, budgetUsdMonthly: Number("unlimited") }); // NaN, throws

// after:
const pod = validatePodTemplate(parsed);
selectAgentBackend(pod); // budgetUsdMonthly is a finite number
Defensive patterns

Strategy: validation

Validate before calling

import { validatePodTemplate } from '@claude-flow/cli/business-pods/pod-schema';

// validatePodTemplate's requireNumber() enforces Number.isFinite for
// budgetUsdMonthly before selectAgentBackend ever sees the value.
const pod = validatePodTemplate(parsedJson);
selectAgentBackend(pod);

Type guard

function hasValidBudgetUsdMonthly(pod: unknown): boolean {
  if (typeof pod !== 'object' || pod === null) return false;
  const v = (pod as { budgetUsdMonthly?: unknown }).budgetUsdMonthly;
  return typeof v === 'number' && Number.isFinite(v);
}

Prevention

When it happens

Trigger: Calling selectAgentBackend(pod) where pod.budgetUsdMonthly is undefined, a string like "50", NaN, or Infinity.

Common situations: Budget read from config as a string and not coerced; a missing field in a hand-written template; Number('unlimited') producing NaN; a computation that overflows to Infinity.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/83ee168052968aae. Report an issue: GitHub.