JuliusBrussee/caveman · warning

filter ${key} must be a finite number

Error message

filter ${key} must be a finite number

What it means

Numeric range filters (min_cost_usd, max_cost_usd, min_total_tokens, max_total_tokens, min_latency_ms, max_latency_ms) must be finite JavaScript numbers when present. Strings ("0.5"), NaN, Infinity, null, or booleans throw with the key named.

Source

Thrown at packages/cli/src/agent-mcp.ts:481

    "member_user_id",
    "api_key_id",
    "optimization_id",
    "status_class",
  ];
  for (const key of stringListKeys) {
    const value = filters[key];
    if (value === undefined) continue;
    if (!Array.isArray(value) || value.length === 0 || value.some((entry) => typeof entry !== "string" || entry.trim() === "")) {
      throw new Error(`filter ${key} must be a non-empty array of non-empty strings`);
    }
    if (key === "status_class" && value.some((entry) => !["2xx", "4xx", "5xx"].includes(entry as string))) {
      throw new Error("filter status_class values must be 2xx, 4xx, or 5xx");
    }
  }
  for (const key of ["min_cost_usd", "max_cost_usd", "min_total_tokens", "max_total_tokens", "min_latency_ms", "max_latency_ms"]) {
    const value = filters[key];
    if (value !== undefined && (typeof value !== "number" || !Number.isFinite(value))) {
      throw new Error(`filter ${key} must be a finite number`);
    }
  }
  for (const key of ["has_error", "compressed"]) {
    const value = filters[key];
    if (value !== undefined && typeof value !== "boolean") throw new Error(`filter ${key} must be a boolean`);
  }
  const monitor = filters.monitor;
  if (monitor !== undefined) {
    if (!monitor || typeof monitor !== "object" || Array.isArray(monitor)) throw new Error("filter monitor must be an object");
    const monitorUnknown = Object.keys(monitor).filter((key) => key !== "id" && key !== "verdict");
    if (monitorUnknown.length > 0) throw new Error(`filter monitor has unknown key(s): ${monitorUnknown.sort().join(", ")}`);
    if (typeof monitor.id !== "string" || monitor.id.trim() === "") throw new Error("filter monitor.id is required");
    if (typeof monitor.verdict !== "string" || !["pass", "fail", "error"].includes(monitor.verdict)) {
      throw new Error("filter monitor.verdict must be pass, fail, or error");
    }
  }
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Send raw JSON numbers: min_cost_usd: 0.5, not "0.5".
  2. Coerce and guard at the boundary: Number(v) then Number.isFinite check, drop the filter if not.
  3. Default absent — omit the key rather than sending null/NaN.

Example fix

// before
{ filters: { min_cost_usd: String(threshold) } }

// after
{ filters: { min_cost_usd: Number(threshold) } } // Number.isFinite checked before send
Defensive patterns

Strategy: type-guard

Validate before calling

for (const k of ["min_cost_usd","max_cost_usd","min_total_tokens","max_total_tokens","min_latency_ms","max_latency_ms"]) {
  const v = filters[k];
  if (v !== undefined && (typeof v !== "number" || !Number.isFinite(v))) delete filters[k]; // or throw
}

Type guard

function isFiniteNumber(v: unknown): v is number {
  return typeof v === "number" && Number.isFinite(v);
}

Prevention

When it happens

Trigger: min_cost_usd: "0.5" sent as a string from a form or LLM; NaN produced by a failed parseFloat leaking into args; Infinity from dividing by zero while computing a cap.

Common situations: LLM-generated tool calls quoting numbers; client code reading thresholds from env/config as strings without conversion; computed bounds that can be NaN when a denominator is zero.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/315ca2f59f94d8be. Report an issue: GitHub.