{"record":{"id":"9dc77ceb7c76e5e8","repo":"JuliusBrussee/caveman","slug":"cave-budget-output-floor-invalid","errorCode":null,"errorMessage":"cave_budget_output_floor_invalid","messagePattern":"cave_budget_output_floor_invalid","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/budget.ts","lineNumber":112,"sourceCode":"  const denomination: BudgetDenomination = usd ? \"usd\" : \"tokens\";\n  const max = usd ? budget.maxUsd! : budget.maxTokens!;\n  if (!Number.isFinite(max) || max <= 0) throw new Error(\"cave_budget_max_invalid\");\n  if (denomination === \"tokens\" && !Number.isSafeInteger(max)) {\n    throw new Error(\"cave_budget_max_invalid\");\n  }\n  const wrongInitial = denomination === \"usd\" ? budget.initialTokens : budget.initialUsd;\n  if (wrongInitial !== undefined) throw new Error(\"cave_budget_denomination_ambiguous\");\n  const declaredInitial = denomination === \"usd\" ? budget.initialUsd : budget.initialTokens;\n  const initial = declaredInitial ?? max;\n  if (!Number.isFinite(initial) || initial <= 0 || initial > max) {\n    throw new Error(\"cave_budget_initial_invalid\");\n  }\n  if (denomination === \"tokens\" && !Number.isSafeInteger(initial)) {\n    throw new Error(\"cave_budget_initial_invalid\");\n  }\n  const outputFloorTokens = budget.outputFloorTokens ?? OUTPUT_CLAMP_FLOOR_TOKENS;\n  if (!Number.isSafeInteger(outputFloorTokens) || outputFloorTokens <= 0) {\n    throw new Error(\"cave_budget_output_floor_invalid\");\n  }\n  const onExhausted = budget.onExhausted ?? \"compact\";\n  if (onExhausted !== \"compact\" && onExhausted !== \"stop\") {\n    throw new Error(\"cave_budget_on_exhausted_invalid\");\n  }\n  return Object.freeze({\n    denomination,\n    max,\n    initial,\n    outputFloorTokens,\n    onExhausted,\n    compaction: normalizeCompaction(budget.compaction),\n  });\n}\n\n/** Identity and shape of one prospective provider call. */\nexport interface CallCeiling {\n  readonly provider: string;","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/budget.ts#L94-L130","documentation":"Thrown by normalizeRunBudget when outputFloorTokens is not a safe integer or is not positive. The output floor is the minimum output-token headroom the runtime reserves on each call; a zero, negative, fractional, or non-numeric floor makes that clamp meaningless, so it is rejected. The field is optional and defaults to OUTPUT_CLAMP_FLOOR_TOKENS, so this only fires when a caller overrides it badly.","triggerScenarios":"Passing outputFloorTokens: 0 (attempting to disable the clamp); passing a float like 512.5; passing NaN or a negative number; passing a numeric string such as \"1024\".","commonSituations":"Tuning the floor from an env var without validating the parse; copying a fractional default from documentation or a blog post; attempting to disable output clamping entirely by setting the floor to zero.","solutions":["If you need a different floor, pass a positive safe integer (e.g. outputFloorTokens: 2048).","To use the built-in default, omit the field entirely rather than passing 0.","Validate env-derived values: Number.isSafeInteger(n) && n > 0 before including them in the budget."],"exampleFix":"// before\nconst budget = { maxUsd: 5, outputFloorTokens: 0 }; // attempting to disable the floor\n\n// after\nconst budget = { maxUsd: 5, outputFloorTokens: 2048 };","handlingStrategy":"validation","validationCode":"const floor = process.env.OUTPUT_FLOOR !== undefined ? Number(process.env.OUTPUT_FLOOR) : undefined;\nconst budget = {\n  maxUsd: 5,\n  ...(floor !== undefined && Number.isSafeInteger(floor) && floor > 0 ? { outputFloorTokens: floor } : {}),\n};","typeGuard":"function isValidOutputFloor(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isSafeInteger(v) && v > 0;\n}","tryCatchPattern":null,"preventionTips":["Omit outputFloorTokens to use the built-in default; do not pass 0.","Validate env-sourced floors with Number.isSafeInteger && > 0.","Remember the floor clamps output token caps per call; a value larger than your max is also suspicious."],"tags":["budget","validation","output-tokens","config"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}