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

  1. Log the offending action.type to find which dispatch site sends the unknown action.
  2. Fix the dispatch site to use a valid action type from the Action union in ./actions.
  3. If a new action was added, add a case for it in templateReducer.
  4. 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

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


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