{"record":{"id":"06254c93c8de2e9e","repo":"actualbudget/actual","slug":"amount-to-hold-needs-to-be-greater-than-0","errorCode":null,"errorMessage":"Amount to hold needs to be greater than 0","messagePattern":"Amount to hold needs to be greater than 0","errorType":"validation","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/api.ts","lineNumber":503,"sourceCode":"}) {\n  checkFileOpen();\n  await validateMonth(month);\n  await validateExpenseCategory('budget-set-carryover', categoryId);\n  return handlers['budget/set-carryover']({\n    startMonth: month,\n    category: categoryId,\n    flag,\n  });\n});\n\nhandlers['api/budget-hold-for-next-month'] = withMutation(async function ({\n  month,\n  amount,\n}) {\n  checkFileOpen();\n  await validateMonth(month);\n  if (amount <= 0) {\n    throw APIError('Amount to hold needs to be greater than 0');\n  }\n  return handlers['budget/hold-for-next-month']({\n    month,\n    amount,\n  });\n});\n\nhandlers['api/budget-reset-hold'] = withMutation(async function ({ month }) {\n  checkFileOpen();\n  await validateMonth(month);\n  return handlers['budget/reset-hold']({ month });\n});\n\nhandlers['api/transactions-export'] = async function ({\n  transactions,\n  categoryGroups,\n  payees,\n  accounts,","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/api.ts#L485-L521","documentation":"api.holdForNextMonth() carries leftover budgeted money for a category into the next month. A hold only makes sense for a positive amount, so the handler rejects amount <= 0 with this APIError before delegating to the internal budget handler.","triggerScenarios":"Calling api.holdForNextMonth({ month, categoryId, amount }) with amount = 0, a negative number, or a non-numeric value coerced to <= 0 (e.g. NaN fails the <= 0 check? NaN comparison is false, but 0 or negative passes validation fail here).","commonSituations":"Scripts computing a hold from a balance that ended up 0 or negative after overspending; passing an uninitialized variable; unit conversion mistakes (cents vs dollars rounding to 0).","solutions":["Verify the computed amount is > 0 before calling; skip the hold when there is nothing left","Check sign conventions — you may need to negate a 'leftover' value or use a different API (reset/cover) for negative balances","Log the raw amount and its source to find where the 0/negative value originates"],"exampleFix":"// before\nconst leftover = endBalance - reserve;\nawait api.holdForNextMonth(month, categoryId, leftover);\n// after\nconst leftover = endBalance - reserve;\nif (leftover > 0) {\n  await api.holdForNextMonth(month, categoryId, leftover);\n}","handlingStrategy":"validation","validationCode":"if (!(typeof amount === 'number' && amount > 0)) throw APIError('Amount to hold needs to be greater than 0');","typeGuard":"function isPositive(n) {\n  return typeof n === 'number' && Number.isFinite(n) && n > 0;\n}","tryCatchPattern":"try {\n  await holdForNextMonth(args);\n} catch (e) {\n  if (/greater than 0/.test(e.message)) return null;\n  throw e;\n}","preventionTips":["Clamp computed hold amounts to a minimum of 0 and skip when 0","Sanitize numeric inputs from external sources","Document the positivity requirement in the API surface"],"tags":["api","validation","budget"],"backgroundTag":"invalid-argument-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}