JuliusBrussee/caveman · error · Error
cave_budget_max_invalid
Error message
cave_budget_max_invalid
What it means
Thrown by normalizeRunBudget when the chosen budget cap (maxUsd or maxTokens) is not a finite number or is less than or equal to zero. A cap of 0, a negative number, NaN, or Infinity is rejected up front because it is either a misconfiguration or an attempt to run with a meaningless limit.
Source
Thrown at packages/agent/src/budget.ts:96
readonly max: number;
readonly initial: number;
readonly outputFloorTokens: number;
readonly onExhausted: "compact" | "stop";
readonly compaction: NormalizedCompaction;
}
/**
* Validate a caller-supplied budget. Fails closed: an ambiguous, unbounded, or
* self-contradicting budget is rejected before the first provider call rather
* than silently degrading into no cap at all.
*/
export function normalizeRunBudget(budget: RunBudget): NormalizedBudget {
const usd = budget.maxUsd !== undefined;
const tokens = budget.maxTokens !== undefined;
if (usd === tokens) throw new Error("cave_budget_denomination_ambiguous");
const denomination: BudgetDenomination = usd ? "usd" : "tokens";
const max = usd ? budget.maxUsd! : budget.maxTokens!;
if (!Number.isFinite(max) || max <= 0) throw new Error("cave_budget_max_invalid");
if (denomination === "tokens" && !Number.isSafeInteger(max)) {
throw new Error("cave_budget_max_invalid");
}
const wrongInitial = denomination === "usd" ? budget.initialTokens : budget.initialUsd;
if (wrongInitial !== undefined) throw new Error("cave_budget_denomination_ambiguous");
const declaredInitial = denomination === "usd" ? budget.initialUsd : budget.initialTokens;
const initial = declaredInitial ?? max;
if (!Number.isFinite(initial) || initial <= 0 || initial > max) {
throw new Error("cave_budget_initial_invalid");
}
if (denomination === "tokens" && !Number.isSafeInteger(initial)) {
throw new Error("cave_budget_initial_invalid");
}
const outputFloorTokens = budget.outputFloorTokens ?? OUTPUT_CLAMP_FLOOR_TOKENS;
if (!Number.isSafeInteger(outputFloorTokens) || outputFloorTokens <= 0) {
throw new Error("cave_budget_output_floor_invalid");
}
const onExhausted = budget.onExhausted ?? "compact";View on GitHub (pinned to 27d5a3981a)
Solutions
- Check the value with Number.isFinite(max) && max > 0 before constructing the budget.
- Trace where the cap comes from: if env/CLI, validate and fail with a clear message at load time; if computed, guard the divisor.
- If a zero-cost dry run was intended, use a small positive cap instead of 0 — the library treats 0 as invalid, not as 'free'.
Example fix
// before
const budget = { maxUsd: Number(process.env.RUN_MAX_USD) }; // NaN when unset
// after
const raw = Number(process.env.RUN_MAX_USD);
if (!Number.isFinite(raw) || raw <= 0) {
throw new Error(`RUN_MAX_USD must be a positive number, got ${process.env.RUN_MAX_USD}`);
}
const budget = { maxUsd: raw }; Defensive patterns
Strategy: validation
Validate before calling
function assertPositiveMax(max: number): void {
if (!Number.isFinite(max) || max <= 0) {
throw new Error(`budget max must be finite and > 0, got ${max}`);
}
} Type guard
function isValidBudgetMax(v: unknown): v is number {
return typeof v === "number" && Number.isFinite(v) && v > 0;
} Prevention
- Validate numeric env vars (Number.isFinite && > 0) at load time with a message naming the variable.
- Guard any division used to compute caps against zero divisors.
- Never use 0 to mean 'disabled' — the library rejects it; use a separate flag or omit the run.
When it happens
Trigger: Passing maxUsd: 0 or maxTokens: 0; passing a negative cap; passing NaN (e.g. from parsing an empty string with Number()) or Infinity (e.g. from dividing by zero when computing a cap); passing a numeric string instead of a number so the comparison coerces unexpectedly.
Common situations: Computing a cap from environment variables or CLI args without validating the parse (Number('') is NaN); deriving a per-run cap by dividing a total by a concurrency count that can be zero; JSON config with a typo'd value like "1e3" left as a string in strict-numeric code paths.
Related errors
- cave_budget_denomination_ambiguous
- cave_budget_initial_invalid
- cave_budget_output_floor_invalid
- cave_budget_on_exhausted_invalid
- cave_budget_release_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/d97e4f37117cdb9f.
Report an issue: GitHub.