{"record":{"id":"0a2ac056795fa8cd","repo":"actualbudget/actual","slug":"expected-availablevalue-to-be-a-number-but-got","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/ToBudget.tsx","lineNumber":50,"sourceCode":"}: ToBudgetProps) {\n  const [menuStep, _setMenuStep] = useState<string>('actions');\n  const triggerRef = useRef(null);\n  const format = useFormat();\n\n  const ref = useRef<HTMLSpanElement>(null);\n  const setMenuStep = useCallback(\n    (menu: string) => {\n      if (menu) ref.current?.focus();\n      _setMenuStep(menu);\n    },\n    [ref, _setMenuStep],\n  );\n  const availableValue = useEnvelopeSheetValue({\n    name: envelopeBudget.toBudget,\n    value: 0,\n  });\n  if (typeof availableValue !== 'number' && availableValue !== null) {\n    throw new Error(\n      'Expected availableValue to be a number but got ' + availableValue,\n    );\n  }\n\n  const [menuOpen, setMenuOpen] = useState(false);\n  const [position, setPosition] = useState({ crossOffset: 0, offset: 0 });\n  const resetPosition = (crossOffset = 0, offset = 0) =>\n    setPosition({ crossOffset, offset });\n\n  const handleContextMenu = (e: MouseEvent) => {\n    e.preventDefault();\n    const rect = e.currentTarget.getBoundingClientRect();\n    setPosition({\n      crossOffset: e.clientX - rect.left,\n      offset: e.clientY - rect.bottom,\n    });\n    setMenuOpen(true);\n  };","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx#L32-L68","documentation":"ToBudget reads the 'to budget' amount from the envelope budget sheet via useEnvelopeSheetValue. The hook may legitimately return a number or null (while loading/invalid), but any other type means the sheet cell held bad data. The component guards this invariant and throws `Expected availableValue to be a number but got <value>`. Note the empty message suffix here means the value stringified to empty (e.g. undefined coerced oddly or empty string).","triggerScenarios":"useEnvelopeSheetValue for envelopeBudget.toBudget returns a non-numeric, non-null value — typically undefined (hook not yet bound to a sheet / called outside sheet scope), an empty string cell, or a corrupted/unsynced sheet value.","commonSituations":"Rendering ToBudget before the budget sheet is initialized; month changed while the sheet value factory returns stale data; a bug in a custom sheet value provider returning a string; upgrading loot-core where sheet value typing changed.","solutions":["Ensure the component only renders when the envelope sheet for the month is loaded (check isBudgetInitialized/loading state)","Verify the sheetValue provider for envelopeBudget.toBudget always returns number | null","Coerce defensively at the hook call site: `Number.isFinite(v) ? v : 0`","Clear corrupted budget cache / re-sync the budget if stored sheet data is bad"],"exampleFix":"// before\nconst availableValue = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });\n// after\nconst raw = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });\nconst availableValue = typeof raw === 'number' ? raw : null;","handlingStrategy":"type-guard","validationCode":"const raw = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });\nif (raw !== null && typeof raw !== 'number') {\n  console.warn('toBudget sheet value invalid, skipping render');\n  return null;\n}","typeGuard":"function isSheetNumber(v: unknown): v is number | null {\n  return v === null || (typeof v === 'number' && Number.isFinite(v));\n}","tryCatchPattern":"try {\n  return <ToBudget month={month} />;\n} catch (e) {\n  if (String(e?.message).startsWith('Expected availableValue to be a number')) {\n    return <div className=\"tnum\">0</div>;\n  }\n  throw e;\n}","preventionTips":["Only mount budget summary components after the envelope sheet is initialized/loaded","Keep sheet value factories returning number | null; never return undefined or strings","Use Number.isFinite coercion at hook boundaries instead of throwing deep in components","Add an error boundary around budget summary widgets to degrade gracefully"],"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"}