{"record":{"id":"345c0f293a64f0be","repo":"actualbudget/actual","slug":"invalid-budget-query-dimension-dimension","errorCode":null,"errorMessage":"Invalid BUDGET_QUERY dimension: ${dimension}","messagePattern":"Invalid BUDGET_QUERY dimension: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/hooks/useFormulaExecution.ts","lineNumber":646,"sourceCode":"}\n\n// Helper: Evaluate budget dimension with already-extracted parameters (used by compositional BUDGET_QUERY)\nasync function fetchBudgetDimensionValueDirect(\n  dimension: string,\n  categoryIds: string[],\n  startMonth: string,\n  endMonth: string,\n): Promise<number> {\n  const allowed = new Set([\n    'budgeted',\n    'spent',\n    'balance_start',\n    'balance_end',\n    'goal',\n  ]);\n  const dim = dimension.toLowerCase();\n  if (!allowed.has(dim)) {\n    throw new Error(`Invalid BUDGET_QUERY dimension: ${dimension}`);\n  }\n\n  const intervals = monthUtils.rangeInclusive(startMonth, endMonth);\n\n  // Helper: sum a dimension across all months/categories\n  const sumDimension = async (fieldPattern: string): Promise<number> => {\n    let total = 0;\n    for (const month of intervals) {\n      const monthData = await getMonthBudgetData(month);\n      for (const catId of categoryIds) {\n        total += getMonthDataValue(monthData, fieldPattern, catId) as number;\n      }\n    }\n    return total;\n  };\n\n  if (dim === 'budgeted') {\n    return integerToAmount(await sumDimension('budget-{catId}'), 2);","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/hooks/useFormulaExecution.ts#L628-L664","documentation":"fetchBudgetDimensionValueDirect validates the dimension argument of a BUDGET_QUERY against a fixed allowlist (including 'balance_start', 'balance_end', 'goal', etc.) after lowercasing. If the dimension is not in the set, it throws 'Invalid BUDGET_QUERY dimension: <dimension>'. This guards the direct budget fetch path from unknown or misspelled dimensions.","triggerScenarios":"Calling prefetchBudgetQueries (or code that reaches fetchBudgetDimensionValueDirect) with a BUDGET_QUERY whose dimension string is misspelled, uses different casing with unexpected characters, or is a dimension only supported elsewhere but not in this direct path.","commonSituations":"Typos in a formula like BUDGET_QUERY(..., \"balence\"); copying a dimension name from another API that uses different naming; newly added dimensions not yet in the allowlist; passing a non-string such as a number.","solutions":["Check the spelling of the dimension against the supported list: the allowed set includes 'balance_start', 'balance_end', 'goal' (see the allowlist at useFormulaExecution.ts)","Lowercase/trim the dimension input before calling, since matching is on the lowercased value","Add the new dimension to the allowed set in fetchBudgetDimensionValueDirect if it is a legitimately supported dimension","Validate dimension values at the formula parser layer to give earlier feedback"],"exampleFix":"// before\nawait prefetchBudgetQueries({ dimension: 'BALANCED' });\n// after\nconst VALID = ['sumamount', 'budgeted', 'balance', 'balance_start', 'balance_end', 'goal'];\nconst dim = String(dimension).toLowerCase().trim();\nif (!VALID.includes(dim)) {\n  throw new Error(`Unsupported dimension, use one of: ${VALID.join(', ')}`);\n}\nawait prefetchBudgetQueries({ dimension: dim });","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['sumamount','budgeted','balance','balance_start','balance_end','goal']);\nconst assertDimension = (d: string) => {\n  const dim = String(d).toLowerCase().trim();\n  if (!ALLOWED.has(dim)) throw new Error(`Invalid dimension ${d}; allowed: ${[...ALLOWED].join(', ')}`);\n  return dim;\n};","typeGuard":null,"tryCatchPattern":"try {\n  await prefetchBudgetQueries(args);\n} catch (err) {\n  if (err.message.startsWith('Invalid BUDGET_QUERY dimension')) {\n    showError('Choose one of the supported budget dimensions');\n  } else throw err;\n}","preventionTips":["Keep a single exported constant listing valid dimensions and derive UI dropdowns from it","Always lowercase/trim user input before comparing against the allowlist","Add unit tests covering each valid dimension and one invalid one","When adding dimensions, update the allowlist and docs together"],"tags":["validation","budget","formula"],"backgroundTag":"invalid-enum-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}