actualbudget/actual · error

Unknown template type: ${String(type satisfies undefined)}

Error message

Unknown template type: ${String(type satisfies undefined)}

What it means

getInitialState in the budget goals template editor throws this when the incoming Template's `type` field does not match any of the known template kinds (simple, percentage, schedule, periodic, by, spend, remainder, limit, refill, average, copy, goal). The `type satisfies undefined` assertion means TypeScript considers this branch unreachable for well-typed Templates, so hitting it at runtime indicates a Template object with an unrecognized or missing `type` slipped through from parsed template directives. It is an exhaustiveness guard in the switch that maps a stored template to an editor display state.

Source

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

      return {
        template,
        displayType: 'refill',
      };
    case 'average':
    case 'copy':
      return {
        template,
        displayType: 'historical',
      };
    case 'goal':
      return {
        template,
        displayType: 'goal',
      };
    case 'error':
      throw new Error('An error occurred while parsing the template');
    default:
      throw new Error(
        `Unknown template type: ${String(type satisfies undefined)}`,
      );
  }
};

const changeType = (
  prevState: ReducerState,
  visualType: DisplayTemplateType,
): ReducerState => {
  switch (visualType) {
    case 'limit':
      if (prevState.template.type === 'limit') {
        return prevState;
      }
      return {
        displayType: visualType,
        template: {
          directive: 'template',

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Log the template object to see the offending `type` value (JSON.stringify the template before calling getInitialState).
  2. Add the missing template type as a case in the switch in reducer.ts getInitialState, mapping it to the correct DisplayTemplateType.
  3. Regenerate or fix the template parsing so only valid Template types reach the editor (check the directive parser in loot-core template codegen).
  4. If the template is corrupt user data, filter it out upstream (e.g. skip templates whose type is unknown) instead of passing them to the reducer.

Example fix

// before
default:
  throw new Error(`Unknown template type: ${String(type satisfies undefined)}`);
// after
case 'newKind':
  return { template, displayType: 'newKind' };
default:
  console.error('Unhandled template type', type);
  throw new Error(`Unknown template type: ${String(type)}`);
Defensive patterns

Strategy: type-guard

Validate before calling

const KNOWN = new Set(['simple','percentage','schedule','periodic','by','spend','remainder','limit','refill','average','copy','goal']);
if (!template || !KNOWN.has(template.type)) throw new Error(`Skipping unknown template type: ${template?.type}`);

Type guard

function isKnownTemplateType(t: Template): t is Template & { type: KnownTemplateType } {
  return KNOWN_TEMPLATE_TYPES.has(t.type);
}

Try / catch

try {
  const state = getInitialState(template);
} catch (err) {
  logger.error('Failed to init template editor', template, err);
  return fallbackEditorState;
}

Prevention

When it happens

Trigger: Calling getInitialState (directly or via templateReducer 'set-template' handling / mapTemplateTypesForUpdate) with a Template whose `type` is not one of the handled literal values — e.g. a template parsed from a goal directive that produced a new/renamed type not yet handled, a template deserialized from older budget data, or an object cast to Template without a real `type`.

Common situations: Running a newer editor against templates persisted by an older/newer version with different template type names; custom code or plugins constructing Template objects manually; a parsing bug upstream that yields a template type like 'error' being renamed or a typo'd directive value surviving into the editor.

Related errors


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