actualbudget/actual · error
Unknown display type: ${String(type satisfies never)}
Error message
Unknown display type: ${String(type satisfies never)} What it means
templateReducer is the top-level reducer for the budget goals template editor state. Its default branch throws this when an action's discriminator is not one of the handled action types (e.g. 'set-template', 'update-template', change-type actions). The `type satisfies never` assertion means TypeScript guarantees exhaustiveness over the Action union, so a runtime hit means an action with an unrecognized `type` was dispatched — typically a typo, an action from another reducer, or a stale serialized action.
Source
Thrown at packages/desktop-client/src/components/budget/goals/reducer.ts:364
action: Action,
): ReducerState => {
const type = action.type;
switch (type) {
case 'set-type':
return {
...state,
...changeType(state, action.payload),
};
case 'set-template':
return {
...state,
...getInitialState(action.payload),
};
case 'update-template':
return mapTemplateTypesForUpdate(state, action.payload);
default:
// Make sure we're not missing any cases
throw new Error(`Unknown display type: ${String(type satisfies never)}`);
}
};
View on GitHub (pinned to d4334cb6e6)
Solutions
- Log the offending action.type to find which dispatch site sends the unknown action.
- Fix the dispatch site to use a valid action type from the Action union in ./actions.
- If a new action was added, add a case for it in templateReducer.
- Type the dispatcher with React's Dispatch<Action> so TypeScript rejects unknown action types at compile time.
Example fix
// before
dispatch({ type: 'update-templates' as never });
// after
dispatch({ type: 'update-template', payload: template }); Defensive patterns
Strategy: type-guard
Validate before calling
const ACTION_TYPES = new Set(['set-template','update-template','change-type' /* ...all union members */]); if (!ACTION_TYPES.has(action.type)) return state; // ignore unknown actions
Type guard
function isGoalsAction(a: { type: string }): a is Action {
return GOALS_ACTION_TYPES.has(a.type);
} Try / catch
try {
dispatch(action);
} catch (err) {
logger.error('Bad action for templateReducer', action, err);
} Prevention
- Type useReducer with Dispatch<Action> from ./actions so unknown action literals fail compilation.
- Never use `as any`/`as never` when dispatching; let TypeScript check action shapes.
- Add new action types to the Action union and reducer cases together.
- In tests, iterate every member of the Action union against the reducer to prove exhaustiveness.
When it happens
Trigger: Dispatching an action to templateReducer whose `type` literal is not part of the goals/actions Action union — e.g. calling dispatch with a hand-written object `{ type: 'update-templates' }`, wiring the wrong reducer to a dispatcher, or dispatching actions from a different feature into this reducer.
Common situations: Refactoring action type strings without updating all dispatch sites; copy-pasting a reducer and dispatching foreign actions at it; custom integrations or tests dispatching malformed actions.
Related errors
- Unknown template type: ${String(type satisfies undefined)}
- Unknown display type: ${String(visualType satisfies never)}
- Unhandled action type: ${action.type}
- Unknown display type: ${String(displayType)}
- Unhandled template type
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/b1a4bf1588c2c731.
Report an issue: GitHub.