actualbudget/actual · error
Unknown display type: ${String(visualType satisfies never)}
Error message
Unknown display type: ${String(visualType satisfies never)} What it means
changeType in the goals template reducer throws this when asked to convert the editor to a DisplayTemplateType ('limit', 'refill', 'fixed', 'percentage', 'schedule', 'by', 'remainder', 'historical', 'goal') that is not handled in its switch. The `visualType satisfies never` assertion documents that the default branch is unreachable for valid display types; hitting it means an out-of-range display type value reached the reducer. It is an exhaustiveness check so adding a new display type without a mapping branch fails loudly.
Source
Thrown at packages/desktop-client/src/components/budget/goals/reducer.ts:235
weight: 1,
priority: null,
},
};
case 'goal':
if (prevState.template.type === 'goal') {
return prevState;
}
return {
displayType: visualType,
template: {
directive: 'goal',
type: 'goal',
amount: 1000,
},
};
default:
// Make sure we're not missing any cases
throw new Error(
`Unknown display type: ${String(visualType satisfies never)}`,
);
}
};
function mapTemplateTypesForUpdate(
state: ReducerState,
template: Partial<Template> & Pick<Template, 'type'>,
): ReducerState {
switch (state.template.type) {
case 'average':
switch (template.type) {
case 'copy':
return {
...state,
displayType: 'historical',
template: {
...template,View on GitHub (pinned to d4334cb6e6)
Solutions
- Log the visualType value to identify the unknown display type string.
- Add a case for the missing display type in changeType, returning the appropriate default template shape for it.
- Ensure the UI only dispatches display types that exist in DisplayTemplateType constants.
- If restoring persisted editor state, validate/whitelist the stored displayType before dispatching.
Example fix
// before
default:
throw new Error(`Unknown display type: ${String(visualType satisfies never)}`);
// after
case 'newDisplay':
return { displayType: visualType, template: defaultTemplateFor('newDisplay') };
default:
throw new Error(`Unknown display type: ${String(visualType)}`); Defensive patterns
Strategy: type-guard
Validate before calling
const DISPLAY_TYPES = ['limit','refill','fixed','percentage','schedule','by','remainder','historical','goal'] as const; if (!DISPLAY_TYPES.includes(visualType as never)) return prevState;
Type guard
function isDisplayTemplateType(v: string): v is DisplayTemplateType {
return ['limit','refill','fixed','percentage','schedule','by','remainder','historical','goal'].includes(v);
} Try / catch
try {
dispatch({ type: 'change-type', displayType: visualType });
} catch (err) {
logger.error('Unknown display type change', visualType, err);
} Prevention
- Derive changeType's switch cases from the DisplayTemplateType union so tsc flags missing branches.
- Whitelist displayType values read from persisted editor state before dispatching.
- Never construct display-type strings by hand in UI code; import the constants.
When it happens
Trigger: Dispatching a change-type action through templateReducer with a visualType string that is not one of the DisplayTemplateType literals — e.g. a typo, a stale serialized state, or a new display type added to constants.ts without a matching case in changeType.
Common situations: Developers adding a new template display type to DisplayTemplateType but forgetting to extend changeType; UI code passing a display type derived from user input or persisted state that predates a rename.
Related errors
- Unknown template type: ${String(type satisfies undefined)}
- 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/39326f66b8b98b07.
Report an issue: GitHub.