actualbudget/actual · error

Template cannot be null

Error message

Template cannot be null

What it means

getInitialState builds ReducerState from a parsed Template and explicitly rejects null. Callers are expected to pass a template parsed from a category's notes; null means there is nothing to edit, so the function fails fast instead of returning partial state.

Source

Thrown at packages/desktop-client/src/components/budget/goals/reducer.ts:16

import {
  addMonths,
  dayFromDate,
  firstDayOfMonth,
  monthFromDate,
} from '@actual-app/core/shared/months';
import type { Template } from '@actual-app/core/types/models/templates';

import type { Action } from './actions';
import type { DisplayTemplateType, ReducerState } from './constants';

export const DEFAULT_PRIORITY = 1;

export const getInitialState = (template: Template | null): ReducerState => {
  if (!template) {
    throw new Error('Template cannot be null');
  }
  const type = template.type;
  switch (type) {
    case 'simple':
      return {
        template: {
          type: 'periodic',
          amount: template.monthly ?? 0,
          period: {
            period: 'month',
            amount: 1,
          },
          starting: firstDayOfMonth(new Date()),
          priority: template.priority,
          directive: template.directive,
        },
        displayType: 'fixed',
      };

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Only call getInitialState when a template has been parsed and exists
  2. Guard the dispatch site: check template != null before dispatching 'set-template'
  3. Fix the upstream parser so valid template notes produce a Template object, not null

Example fix

// before
dispatch({ type: 'set-template', payload: parsedTemplate });
// after
if (parsedTemplate) {
  dispatch({ type: 'set-template', payload: parsedTemplate });
}
Defensive patterns

Strategy: validation

Validate before calling

if (template == null) {
  return; // or show an empty-editor state instead of dispatching
}
dispatch({ type: 'set-template', payload: template });

Type guard

const hasTemplate = (t: Template | null | undefined): t is Template =>
  t != null && typeof t.type === 'string';

Try / catch

try {
  dispatch({ type: 'set-template', payload: template });
} catch (e) {
  if (e instanceof Error && e.message === 'Template cannot be null') {
    notify('No template found on this category.');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getInitialState(null), which happens when templateReducer receives a 'set-template' action with null payload, or mapTemplateTypesForUpdate builds a merged template that is null/undefined.

Common situations: Opening the template editor for a category with no template without checking first; a parsing step returning null on malformed notes and its result passed straight to the reducer; race where the template is cleared while the editor is initializing.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/21e5894196cea9e5. Report an issue: GitHub.