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
- Log the template object to see the offending `type` value (JSON.stringify the template before calling getInitialState).
- Add the missing template type as a case in the switch in reducer.ts getInitialState, mapping it to the correct DisplayTemplateType.
- Regenerate or fix the template parsing so only valid Template types reach the editor (check the directive parser in loot-core template codegen).
- 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
- Keep the Template type union in loot-core as the single source of truth and switch on it exhaustively.
- Validate templates parsed from category notes before they enter UI state.
- When adding a template type, update both the parser and every consumer switch in the same PR.
- Test getInitialState against all template types serialized by older app versions.
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
- Unknown display type: ${String(visualType satisfies never)}
- Unknown display type: ${String(displayType)}
- Unknown display type: ${String(type satisfies never)}
- Unhandled template type
- Unhandled action type: ${action.type}
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/4b0f521784eb1f45.
Report an issue: GitHub.