{"record":{"id":"6f45acbc983b4d23","repo":"JuliusBrussee/caveman","slug":"caveman-agent-subagent-maxcostusd-must-be-positiv","errorCode":null,"errorMessage":"caveman agent: subagent maxCostUsd must be positive","messagePattern":"caveman agent: subagent maxCostUsd must be positive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/index.ts","lineNumber":178,"sourceCode":"   * This child's wallet in tokens — the denomination sibling of `maxCostUsd`,\n   * used by a token-metered run. A token-metered run cannot fund a subagent\n   * that declares no token wallet.\n   */\n  maxTokens?: number;\n  maxContextTokens?: number;\n}): ToolDefinition {\n  const maxInputChars = options.maxInputChars ?? 32_768;\n  if (!Number.isSafeInteger(maxInputChars) || maxInputChars <= 0) {\n    throw new Error(\"caveman agent: subagent maxInputChars must be a positive integer\");\n  }\n  const maxCalls = options.maxCalls ?? 1;\n  const maxCostUsd = options.maxCostUsd ?? 1;\n  const maxContextTokens = options.maxContextTokens ?? 128_000;\n  if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) {\n    throw new Error(\"caveman agent: subagent maxCalls must be a positive integer\");\n  }\n  if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {\n    throw new Error(\"caveman agent: subagent maxCostUsd must be positive\");\n  }\n  if (options.maxTokens !== undefined &&\n      (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0)) {\n    throw new Error(\"caveman agent: subagent maxTokens must be a positive integer\");\n  }\n  if (!Number.isSafeInteger(maxContextTokens) || maxContextTokens <= 0) {\n    throw new Error(\"caveman agent: subagent maxContextTokens must be a positive integer\");\n  }\n  return tool({\n    name: options.name,\n    description: options.description,\n    input: schema.object({ task: schema.string() }),\n    effect: \"read\",\n    result: \"auto\",\n    ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),\n    runtime: {\n      kind: \"subagent\",\n      definition: options.agent,","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/index.ts#L160-L196","documentation":"The `subagent()` helper validates `maxCostUsd`, which defaults to 1 and sets the USD spend ceiling for the subagent. It must be a finite number greater than 0; `Number.isFinite` also rejects NaN and Infinity. The library throws at tool-creation time so a misconfigured budget is caught before any agent runs.","triggerScenarios":"Calling `subagent({ maxCostUsd: 0 })`, `subagent({ maxCostUsd: -5 })`, `subagent({ maxCostUsd: Infinity })`, `subagent({ maxCostUsd: NaN })`, or passing a non-numeric config value like `\"unlimited\"`.","commonSituations":"Setting maxCostUsd from a string env var without parsing to float; passing 0 intending 'no budget'; typing Infinity as a sentinel for unlimited spend; unit mix-ups (cents vs dollars leading to 0.x intended as 0).","solutions":["Pass a finite positive number, e.g. `subagent({ maxCostUsd: 0.5 })`, or omit it to use the default of 1.","Parse string config with `Number.parseFloat` and validate `Number.isFinite(v) && v > 0` before calling subagent.","For unlimited spend, pass a very large finite number instead of Infinity or 0."],"exampleFix":"// before\nsubagent({ name: \"worker\", maxCostUsd: process.env.BUDGET })\n// after\nconst maxCostUsd = Number.parseFloat(process.env.BUDGET ?? \"1\");\nif (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) throw new Error(\"BUDGET must be a positive number\");\nsubagent({ name: \"worker\", maxCostUsd })","handlingStrategy":"validation","validationCode":"const assertBudget = (v) => {\n  if (v === undefined) return 1;\n  const n = typeof v === \"string\" ? Number.parseFloat(v) : v;\n  if (!Number.isFinite(n) || n <= 0) throw new TypeError(`maxCostUsd must be positive finite, got ${v}`);\n  return n;\n};\nsubagent({ name: \"worker\", maxCostUsd: assertBudget(process.env.BUDGET) });","typeGuard":"const isPositiveFinite = (v) => typeof v === \"number\" && Number.isFinite(v) && v > 0;","tryCatchPattern":"try {\n  const t = subagent({ maxCostUsd: opts.budget });\n} catch (e) {\n  if (e.message.includes(\"maxCostUsd must be positive\")) {\n    throw new Error(`Invalid BUDGET '${opts.budget}': use a positive number of US dollars`, { cause: e });\n  }\n  throw e;\n}","preventionTips":["Parse monetary config with Number.parseFloat and validate before passing","Document that 0/Infinity are invalid; use a large finite number for effectively unlimited","Keep budgets in a typed config object validated once at startup","Watch for NaN: any failed string parse becomes NaN, which this check rejects"],"tags":["configuration","validation","argument-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}