{"record":{"id":"ad88a954a4aec6d0","repo":"actualbudget/actual","slug":"unitialised-context-method-called-onbudgetaction-ad88a9","errorCode":null,"errorMessage":"Unitialised context method called: onBudgetAction","messagePattern":"Unitialised context method called: onBudgetAction","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/budget/tracking/TrackingBudgetContext.tsx","lineNumber":16,"sourceCode":"import React, { createContext, useContext } from 'react';\nimport type { ReactNode } from 'react';\n\nimport * as monthUtils from '@actual-app/core/shared/months';\n\ntype TrackingBudgetContextDefinition = {\n  summaryCollapsed: boolean;\n  onBudgetAction: (month: string, action: string, arg?: unknown) => void;\n  onToggleSummaryCollapse: () => void;\n  currentMonth: string;\n};\n\nconst TrackingBudgetContext = createContext<TrackingBudgetContextDefinition>({\n  summaryCollapsed: false,\n  onBudgetAction: () => {\n    throw new Error('Unitialised context method called: onBudgetAction');\n  },\n  onToggleSummaryCollapse: () => {\n    throw new Error(\n      'Unitialised context method called: onToggleSummaryCollapse',\n    );\n  },\n  currentMonth: 'unknown',\n});\n\ntype TrackingBudgetProviderProps = Omit<\n  TrackingBudgetContextDefinition,\n  'currentMonth'\n> & {\n  children: ReactNode;\n};\nexport function TrackingBudgetProvider({\n  summaryCollapsed,\n  onBudgetAction,","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/budget/tracking/TrackingBudgetContext.tsx#L1-L34","documentation":"TrackingBudgetContext is created with placeholder defaults: onBudgetAction is a stub function that throws when called. The error means a component called the context's onBudgetAction without a TrackingBudgetContext.Provider being mounted above it (or the provider received no real implementation), so the consumer fell back to the createContext defaults.","triggerScenarios":"Calling onBudgetAction(...) from useTrackingBudget (or useContext(TrackingBudgetContext)) in a component rendered outside of the TrackingBudgetContext.Provider that supplies the real handler, e.g. a budget widget rendered under the non-tracking (report/basic) budget layout.","commonSituations":"Moving a component that uses tracking-budget hooks into the envelope/budget-rollover layout which uses a different context; testing a component in isolation without wrapping it in the provider; a refactor that renamed or removed the Provider while consumers stayed.","solutions":["Wrap the consuming component tree in TrackingBudgetContext.Provider passing a real onBudgetAction implementation.","Ensure the component is rendered inside the tracking-budget layout where the provider lives (packages/desktop-client/src/components/budget/tracking/TrackingBudgetContext.tsx).","In tests, wrap the component under test with the provider (or a mock with jest.fn()/vi.fn() as onBudgetAction).","In the hook, add a dev-time guard that reports the context was consumed without its provider."],"exampleFix":"// before\nexport function MyWidget() {\n  const { onBudgetAction } = useTrackingBudget(); // throws when called\n\n// after\nexport function MyWidget() {\n  return (\n    <TrackingBudgetContext.Provider value={{ summaryCollapsed: false, onBudgetAction: handleBudgetAction, onToggleSummaryCollapse: toggle, currentMonth: month }}>\n      <WidgetInner />\n    </TrackingBudgetContext.Provider>\n  );","handlingStrategy":"try-catch","validationCode":"// guard before calling:\nconst ctx = useContext(TrackingBudgetContext);\nconst isProviderMissing = ctx.onBudgetAction === TrackingBudgetContext._currentValue?.onBudgetAction; // or expose a hasProvider flag on the context value\nif (!ctx || typeof ctx.onBudgetAction !== 'function') throw new Error('TrackingBudgetContext provider missing');","typeGuard":"function hasTrackingBudgetContext(ctx: TrackingBudgetContextDefinition | null): ctx is TrackingBudgetContextDefinition {\n  return ctx != null && typeof ctx.onBudgetAction === 'function' && typeof ctx.onToggleSummaryCollapse === 'function';\n}","tryCatchPattern":"try {\n  onBudgetAction('budget-set-amount', payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Unitialised context method called')) {\n    logger.warn('TrackingBudgetContext not provided; budget action skipped');\n  } else {\n    throw err;\n  }\n}","preventionTips":["Return the context via a custom hook (useTrackingBudget) that throws at render time when the default stubs are detected.","Always render consumers under the tracking-budget layout that owns the Provider.","In tests/Storybook, use a shared wrapper that supplies the provider."],"tags":["react","react-context","missing-provider","budget"],"backgroundTag":"context-used-outside-provider","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}