JuliusBrussee/caveman · error · Error

caveman agent: subagent maxCostUsd must be positive

Error message

caveman agent: subagent maxCostUsd must be positive

What it means

Thrown by the subagent tool factory when options.maxCostUsd is not a finite positive number. maxCostUsd is a hard spending ceiling for one subagent tool call; zero, negative, NaN, or Infinity values cannot bound spend. It is deliberately slightly looser than the integer checks: any finite positive number (including decimals) is accepted.

Source

Thrown at packages/agent/src/index.ts:178

   * This child's wallet in tokens — the denomination sibling of `maxCostUsd`,
   * used by a token-metered run. A token-metered run cannot fund a subagent
   * that declares no token wallet.
   */
  maxTokens?: number;
  maxContextTokens?: number;
}): ToolDefinition {
  const maxInputChars = options.maxInputChars ?? 32_768;
  if (!Number.isSafeInteger(maxInputChars) || maxInputChars <= 0) {
    throw new Error("caveman agent: subagent maxInputChars must be a positive integer");
  }
  const maxCalls = options.maxCalls ?? 1;
  const maxCostUsd = options.maxCostUsd ?? 1;
  const maxContextTokens = options.maxContextTokens ?? 128_000;
  if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) {
    throw new Error("caveman agent: subagent maxCalls must be a positive integer");
  }
  if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {
    throw new Error("caveman agent: subagent maxCostUsd must be positive");
  }
  if (options.maxTokens !== undefined &&
      (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0)) {
    throw new Error("caveman agent: subagent maxTokens must be a positive integer");
  }
  if (!Number.isSafeInteger(maxContextTokens) || maxContextTokens <= 0) {
    throw new Error("caveman agent: subagent maxContextTokens must be a positive integer");
  }
  return tool({
    name: options.name,
    description: options.description,
    input: schema.object({ task: schema.string() }),
    effect: "read",
    result: "auto",
    ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
    runtime: {
      kind: "subagent",
      definition: options.agent,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Pass a finite positive dollar amount, e.g. maxCostUsd: 2.5
  2. Validate config-sourced values before the call: Number.isFinite(v) && v > 0 ? v : 1
  3. If you intended 'no limit', pick the largest ceiling you actually accept — the library requires a positive finite bound
  4. Omit maxCostUsd entirely to use the default of 1 USD

Example fix

// before
subagentTool({ agent, name: "worker", maxCostUsd: Number(cfg.maxCost) }); // Number("") === 0 -> NaN/0

// after
const cost = Number(cfg.maxCost);
subagentTool({
  agent,
  name: "worker",
  maxCostUsd: Number.isFinite(cost) && cost > 0 ? cost : 1,
});
Defensive patterns

Strategy: validation

Validate before calling

const maxCostUsd = Number(cfg.maxCost);
if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {
  throw new Error(`maxCostUsd must be a finite positive dollar amount, got ${JSON.stringify(cfg.maxCost)}`);
}

Type guard

const isPositiveFinite = (v: unknown): v is number =>
  typeof v === "number" && Number.isFinite(v) && v > 0;

Try / catch

try {
  subagentTool({ agent, name: "worker", maxCostUsd });
} catch (e) {
  if (e instanceof Error && e.message.includes("maxCostUsd")) { /* re-prompt operator for budget */ }
  throw e;
}

Prevention

When it happens

Trigger: Passing maxCostUsd: 0 (a common mistake meaning 'no budget'), a negative number, NaN (e.g. from parsing a non-numeric env var), or Infinity. The default is 1 (USD); omission is fine.

Common situations: Wiring cost caps from user config where an empty string parses to NaN, using 0 to try to disable the cap, or misreading the unit (it is dollars, not cents) and passing 0.01-equivalent values like 1 when meaning 100.

Related errors


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