{"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":"Error","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/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/index.ts#L160-L196","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a finite positive dollar amount, e.g. maxCostUsd: 2.5","Validate config-sourced values before the call: Number.isFinite(v) && v > 0 ? v : 1","If you intended 'no limit', pick the largest ceiling you actually accept — the library requires a positive finite bound","Omit maxCostUsd entirely to use the default of 1 USD"],"exampleFix":"// before\nsubagentTool({ agent, name: \"worker\", maxCostUsd: Number(cfg.maxCost) }); // Number(\"\") === 0 -> NaN/0\n\n// after\nconst cost = Number(cfg.maxCost);\nsubagentTool({\n  agent,\n  name: \"worker\",\n  maxCostUsd: Number.isFinite(cost) && cost > 0 ? cost : 1,\n});","handlingStrategy":"validation","validationCode":"const maxCostUsd = Number(cfg.maxCost);\nif (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {\n  throw new Error(`maxCostUsd must be a finite positive dollar amount, got ${JSON.stringify(cfg.maxCost)}`);\n}","typeGuard":"const isPositiveFinite = (v: unknown): v is number =>\n  typeof v === \"number\" && Number.isFinite(v) && v > 0;","tryCatchPattern":"try {\n  subagentTool({ agent, name: \"worker\", maxCostUsd });\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"maxCostUsd\")) { /* re-prompt operator for budget */ }\n  throw e;\n}","preventionTips":["Document the unit (USD) next to every maxCostUsd config field","Reject empty config values before Number() — Number(\"\") is 0 and Number(\"abc\") is NaN","Remember 0 is never valid: this library has no unlimited mode"],"tags":["subagent","validation","cost-limits","configuration"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}