{"record":{"id":"5f5528d7b6f1ed5b","repo":"JuliusBrussee/caveman","slug":"caveman-build-maxsearchcostusd-must-be-positive","errorCode":null,"errorMessage":"caveman build: maxSearchCostUsd must be positive","messagePattern":"caveman build: maxSearchCostUsd must be positive","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/build.ts","lineNumber":53,"sourceCode":"    efficiency: options.efficiency ?? \"max\",\n    requiredFixturePassRate: options.requiredFixturePassRate ?? 1,\n    qualityRetention: options.qualityRetention ?? 0.98,\n    maxSearchCostUsd: options.maxSearchCostUsd ?? 2,\n    lock: options.lock ?? \"strict\",\n    sandbox: options.sandbox ?? \"required\",\n    ...(options.allowedModels === undefined ? {} : { allowedModels: [...options.allowedModels] }),\n    ...(options.deniedModels === undefined ? {} : { deniedModels: [...options.deniedModels] }),\n    ...(options.maxP95LatencyMs === undefined ? {} : { maxP95LatencyMs: options.maxP95LatencyMs }),\n    ...(options.forbiddenSafetyClasses === undefined ? {} : { forbiddenSafetyClasses: [...options.forbiddenSafetyClasses] }),\n  };\n  if (!(config.requiredFixturePassRate > 0 && config.requiredFixturePassRate <= 1)) {\n    throw new Error(\"caveman build: requiredFixturePassRate must be in (0,1]\");\n  }\n  if (!(config.qualityRetention > 0 && config.qualityRetention <= 1)) {\n    throw new Error(\"caveman build: qualityRetention must be in (0,1]\");\n  }\n  if (!(config.maxSearchCostUsd > 0)) {\n    throw new Error(\"caveman build: maxSearchCostUsd must be positive\");\n  }\n  return Object.freeze(config);\n}\n\nexport interface CavePlan {\n  schema_version: 1;\n  plan_id: string;\n  model: string;\n  reasoning: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\";\n  segment_routes: Array<{\n    segment_kind: ContextKind;\n    segment_id?: string;\n    transform_id: string;\n    fallback: \"original\";\n  }>;\n  budgets: {\n    instructions: number;\n    tools: number;","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/build.ts#L35-L71","documentation":"defineBuild requires maxSearchCostUsd to be strictly positive because it caps the dollar budget of candidate-plan search; zero or negative caps would permit no search or imply refunds. The check runs synchronously before the frozen BuildConfig is returned.","triggerScenarios":"Passing maxSearchCostUsd <= 0 (0, negative), or NaN (the >0 check fails for NaN), when calling defineBuild.","commonSituations":"Leaving a placeholder 0 in a config; a currency/units mixup (cents vs dollars) producing a tiny or negative number; a computed budget that underflows to 0 or NaN.","solutions":["Set maxSearchCostUsd to a positive dollar amount, e.g. 5","Sanity-check the units: the field is USD, not cents","Validate computed budgets with Number.isFinite and > 0 before defineBuild","Replace 0 placeholders with a real cap or omit if optional"],"exampleFix":"// before\ndefineBuild({ entry, evals, maxSearchCostUsd: 0 });\n// after\ndefineBuild({ entry, evals, maxSearchCostUsd: 5 });","handlingStrategy":"validation","validationCode":"const c = options.maxSearchCostUsd;\nif (!(Number.isFinite(c) && c > 0)) {\n  throw new Error(`maxSearchCostUsd must be a positive USD amount, got ${c}`);\n}","typeGuard":"function isValidCost(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v) && v > 0;\n}","tryCatchPattern":null,"preventionTips":["Document that the field is USD, not cents","Replace 0 placeholders with real caps or omit optional fields","Validate monetary config values at load time"],"tags":["config","validation","budget"],"backgroundTag":"invalid-config-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"}