{"record":{"id":"663d18bba70f9b0b","repo":"actualbudget/actual","slug":"expected-availablevalue-to-be-a-number-but-got-663d18","errorCode":null,"errorMessage":"Expected availableValue to be a number but got ","messagePattern":"Expected availableValue to be a number but got ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudgetAmount.tsx","lineNumber":49,"sourceCode":"\nexport function ToBudgetAmount({\n  prevMonthName,\n  style,\n  amountStyle,\n  onClick,\n  isTotalsListTooltipDisabled = false,\n  onContextMenu,\n}: ToBudgetAmountProps) {\n  const { t } = useTranslation();\n  const sheetName = useEnvelopeSheetName(envelopeBudget.toBudget);\n  const sheetValue = useEnvelopeSheetValue({\n    name: envelopeBudget.toBudget,\n    value: 0,\n  });\n  const format = useFormat();\n  const availableValue = sheetValue;\n  if (typeof availableValue !== 'number' && availableValue !== null) {\n    throw new Error(\n      'Expected availableValue to be a number but got ' + availableValue,\n    );\n  }\n  const num = availableValue ?? 0;\n  const isNegative = num < 0;\n  const isPositive = num > 0;\n\n  return (\n    <View style={{ alignItems: 'center', ...style }}>\n      <Block>{isNegative ? t('Overbudgeted:') : t('To Budget:')}</Block>\n      <View>\n        <Tooltip\n          content={\n            <TotalsList\n              prevMonthName={prevMonthName}\n              style={{\n                padding: 7,\n              }}","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudgetAmount.tsx#L31-L67","documentation":"ToBudgetAmount displays the 'to budget' figure and applies the same invariant as ToBudget: the sheet value (aliased to availableValue) must be a number or null; anything else throws `Expected availableValue to be a number but got <value>`. An empty suffix indicates the offending value stringified to empty, most commonly undefined.","triggerScenarios":"useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 }) returns undefined or a non-numeric string — the sheet is not yet initialized for the month, the value factory is missing, or stored sheet data is corrupted.","commonSituations":"Component mounted before budget data loads; navigating months while the sheet recomputes; custom/older sheet value code returning strings; merge/sync producing an invalid cell value.","solutions":["Gate rendering on budget sheet initialization so the component never reads a not-yet-bound sheet value","Check that the toBudget sheet value producer returns number | null and not undefined/string","Coerce at the call site with Number.isFinite before the guard","Re-sync or clear the local budget database if the persisted value is corrupt"],"exampleFix":"// before\nconst availableValue = sheetValue;\nif (typeof availableValue !== 'number' && availableValue !== null) {\n  throw new Error('Expected availableValue to be a number but got ' + availableValue);\n}\n// after\nconst availableValue = Number.isFinite(sheetValue) ? sheetValue : null;\nconst num = availableValue ?? 0;","handlingStrategy":"type-guard","validationCode":"const availableValue = Number.isFinite(sheetValue) ? sheetValue : null;\nif (availableValue === null) {\n  // render loading/zero state instead of proceeding\n}","typeGuard":"function isNumberOrNull(v: unknown): v is number | null {\n  return v === null || (typeof v === 'number' && Number.isFinite(v));\n}","tryCatchPattern":"try {\n  return <ToBudgetAmount month={month} />;\n} catch (e) {\n  if (String(e?.message).includes('Expected availableValue to be a number')) {\n    return <Text className=\"tnum\">0</Text>;\n  }\n  throw e;\n}","preventionTips":["Guard against undefined before the invariant check: treat undefined like null (loading state)","Verify the sheet value binding for envelopeBudget.toBudget exists for the current month","Normalize all sheet reads through one helper that guarantees number | null","Cover month-navigation races with tests that mount the component before data loads"],"tags":["type-mismatch","undefined-value","budget-sheet","react"],"backgroundTag":"unexpected-undefined-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}